From cc468e116f264ed7e18fb95e3e581d85ab848240 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 6 Dec 2024 16:41:19 +0530 Subject: [PATCH 01/32] setup skeleton --- itest/bitcoind_node_setup.go | 111 +++++ itest/containers/config.go | 24 ++ itest/containers/containers.go | 186 +++++++++ itest/e2e_test.go | 703 ++++++++++++++++++++++++++++++++ itest/queue_setup.go | 61 +++ itest/scripts/start_rabbitmq.sh | 11 + itest/test-params.json | 38 ++ itest/test_manager.go | 436 ++++++++++++++++++++ 8 files changed, 1570 insertions(+) create mode 100644 itest/bitcoind_node_setup.go create mode 100644 itest/containers/config.go create mode 100644 itest/containers/containers.go create mode 100644 itest/e2e_test.go create mode 100644 itest/queue_setup.go create mode 100755 itest/scripts/start_rabbitmq.sh create mode 100644 itest/test-params.json create mode 100644 itest/test_manager.go diff --git a/itest/bitcoind_node_setup.go b/itest/bitcoind_node_setup.go new file mode 100644 index 0000000..de90e1c --- /dev/null +++ b/itest/bitcoind_node_setup.go @@ -0,0 +1,111 @@ +package e2etest + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/babylonlabs-io/staking-indexer/itest/containers" +) + +var ( + startTimeout = 30 * time.Second +) + +type CreateWalletResponse struct { + Name string `json:"name"` + Warning string `json:"warning"` +} + +type GenerateBlockResponse struct { + // address of the recipient of rewards + Address string `json:"address"` + // blocks generated + Blocks []string `json:"blocks"` +} + +type BitcoindTestHandler struct { + t *testing.T + m *containers.Manager +} + +func NewBitcoindHandler(t *testing.T) *BitcoindTestHandler { + m, err := containers.NewManager() + require.NoError(t, err) + return &BitcoindTestHandler{ + t: t, + m: m, + } +} + +func (h *BitcoindTestHandler) Start() { + tempPath, err := os.MkdirTemp("", "bitcoind-staker-test-*") + require.NoError(h.t, err) + + h.t.Cleanup(func() { + _ = os.RemoveAll(tempPath) + }) + + _, err = h.m.RunBitcoindResource(tempPath) + require.NoError(h.t, err) + + h.t.Cleanup(func() { + _ = h.m.ClearResources() + }) + + require.Eventually(h.t, func() bool { + _, err := h.GetBlockCount() + h.t.Logf("failed to get block count: %v", err) + return err == nil + }, startTimeout, 500*time.Millisecond, "bitcoind did not start") + +} + +func (h *BitcoindTestHandler) GetBlockCount() (int, error) { + buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"getblockcount"}) + if err != nil { + return 0, err + } + + buffStr := buff.String() + + parsedBuffStr := strings.TrimSuffix(buffStr, "\n") + + num, err := strconv.Atoi(parsedBuffStr) + if err != nil { + return 0, err + } + + return num, nil +} + +func (h *BitcoindTestHandler) CreateWallet(walletName string, passphrase string) *CreateWalletResponse { + // last false on the list will create legacy wallet. This is needed, as currently + // we are signing all taproot transactions by dumping the private key and signing it + // on app level. Descriptor wallets do not allow dumping private keys. + buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"createwallet", walletName, "false", "false", passphrase, "false", "false"}) + require.NoError(h.t, err) + + var response CreateWalletResponse + err = json.Unmarshal(buff.Bytes(), &response) + require.NoError(h.t, err) + + return &response +} + +func (h *BitcoindTestHandler) GenerateBlocks(count int) *GenerateBlockResponse { + buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"-generate", fmt.Sprintf("%d", count)}) + require.NoError(h.t, err) + + var response GenerateBlockResponse + err = json.Unmarshal(buff.Bytes(), &response) + require.NoError(h.t, err) + + return &response +} diff --git a/itest/containers/config.go b/itest/containers/config.go new file mode 100644 index 0000000..2521e78 --- /dev/null +++ b/itest/containers/config.go @@ -0,0 +1,24 @@ +package containers + +// ImageConfig contains all images and their respective tags +// needed for running e2e tests. +type ImageConfig struct { + BitcoindRepository string + BitcoindVersion string +} + +//nolint:deadcode +const ( + dockerBitcoindRepository = "lncm/bitcoind" + dockerBitcoindVersionTag = "v24.0.1" +) + +// NewImageConfig returns ImageConfig needed for running e2e test. +func NewImageConfig() ImageConfig { + config := ImageConfig{ + BitcoindRepository: dockerBitcoindRepository, + BitcoindVersion: dockerBitcoindVersionTag, + } + return config + +} diff --git a/itest/containers/containers.go b/itest/containers/containers.go new file mode 100644 index 0000000..2e4100b --- /dev/null +++ b/itest/containers/containers.go @@ -0,0 +1,186 @@ +package containers + +import ( + "bytes" + "context" + "fmt" + "regexp" + "testing" + "time" + + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "github.com/stretchr/testify/require" +) + +const ( + bitcoindContainerName = "bitcoind-test" +) + +var errRegex = regexp.MustCompile(`(E|e)rror`) + +// Manager is a wrapper around all Docker instances, and the Docker API. +// It provides utilities to run and interact with all Docker containers used within e2e testing. +type Manager struct { + cfg ImageConfig + pool *dockertest.Pool + resources map[string]*dockertest.Resource +} + +// NewManager creates a new Manager instance and initializes +// all Docker specific utilities. Returns an error if initialization fails. +func NewManager() (docker *Manager, err error) { + docker = &Manager{ + cfg: NewImageConfig(), + resources: make(map[string]*dockertest.Resource), + } + docker.pool, err = dockertest.NewPool("") + if err != nil { + return nil, err + } + return docker, nil +} + +func (m *Manager) ExecBitcoindCliCmd(t *testing.T, command []string) (bytes.Buffer, bytes.Buffer, error) { + // this is currently hardcoded, as it will be the same for all tests + cmd := []string{"bitcoin-cli", "-chain=regtest", "-rpcuser=user", "-rpcpassword=pass"} + cmd = append(cmd, command...) + return m.ExecCmd(t, bitcoindContainerName, cmd) +} + +// ExecCmd executes command by running it on the given container. +// It word for word `error` in output to discern between error and regular output. +// It retures stdout and stderr as bytes.Buffer and an error if the command fails. +func (m *Manager) ExecCmd(t *testing.T, containerName string, command []string) (bytes.Buffer, bytes.Buffer, error) { + if _, ok := m.resources[containerName]; !ok { + return bytes.Buffer{}, bytes.Buffer{}, fmt.Errorf("no resource %s found", containerName) + } + containerId := m.resources[containerName].Container.ID + + var ( + outBuf bytes.Buffer + errBuf bytes.Buffer + ) + + timeout := 20 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + t.Logf("\n\nRunning: \"%s\"", command) + + // We use the `require.Eventually` function because it is only allowed to do one transaction per block without + // sequence numbers. For simplicity, we avoid keeping track of the sequence number and just use the `require.Eventually`. + require.Eventually( + t, + func() bool { + exec, err := m.pool.Client.CreateExec(docker.CreateExecOptions{ + Context: ctx, + AttachStdout: true, + AttachStderr: true, + Container: containerId, + User: "root", + Cmd: command, + }) + + if err != nil { + t.Logf("failed to create exec: %v", err) + return false + } + + err = m.pool.Client.StartExec(exec.ID, docker.StartExecOptions{ + Context: ctx, + Detach: false, + OutputStream: &outBuf, + ErrorStream: &errBuf, + }) + if err != nil { + t.Logf("failed to start exec: %v", err) + return false + } + + errBufString := errBuf.String() + // Note that this does not match all errors. + // This only works if CLI outputs "Error" or "error" + // to stderr. + if errRegex.MatchString(errBufString) { + t.Log("\nstderr:") + t.Log(errBufString) + + t.Log("\nstdout:") + t.Log(outBuf.String()) + return false + } + + return true + }, + timeout, + 500*time.Millisecond, + "command failed", + ) + + return outBuf, errBuf, nil +} + +func (m *Manager) RunBitcoindResource( + bitcoindCfgPath string, +) (*dockertest.Resource, error) { + bitcoindResource, err := m.pool.RunWithOptions( + &dockertest.RunOptions{ + Name: bitcoindContainerName, + Repository: m.cfg.BitcoindRepository, + Tag: m.cfg.BitcoindVersion, + User: "root:root", + Mounts: []string{ + fmt.Sprintf("%s/:/data/.bitcoin", bitcoindCfgPath), + }, + ExposedPorts: []string{ + "8332", + "8333", + "28332", + "28333", + "18443", + "18444", + }, + PortBindings: map[docker.Port][]docker.PortBinding{ + "8332/tcp": {{HostIP: "", HostPort: "8332"}}, + "8333/tcp": {{HostIP: "", HostPort: "8333"}}, + "28332/tcp": {{HostIP: "", HostPort: "28332"}}, + "28333/tcp": {{HostIP: "", HostPort: "28333"}}, + "18443/tcp": {{HostIP: "", HostPort: "18443"}}, + "18444/tcp": {{HostIP: "", HostPort: "18444"}}, + }, + Cmd: []string{ + "-regtest", + "-txindex", + "-rpcuser=user", + "-rpcpassword=pass", + "-rpcallowip=0.0.0.0/0", + "-rpcbind=0.0.0.0", + }, + }, + noRestart, + ) + if err != nil { + return nil, err + } + m.resources[bitcoindContainerName] = bitcoindResource + return bitcoindResource, nil +} + +// ClearResources removes all outstanding Docker resources created by the Manager. +func (m *Manager) ClearResources() error { + for _, resource := range m.resources { + if err := m.pool.Purge(resource); err != nil { + return err + } + } + + return nil +} + +func noRestart(config *docker.HostConfig) { + // in this case we don't want the nodes to restart on failure + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } +} diff --git a/itest/e2e_test.go b/itest/e2e_test.go new file mode 100644 index 0000000..f37b4b7 --- /dev/null +++ b/itest/e2e_test.go @@ -0,0 +1,703 @@ +//go:build e2e +// +build e2e + +package e2etest + +import ( + "encoding/hex" + "encoding/json" + "math/rand" + "testing" + "time" + + "github.com/babylonlabs-io/babylon/btcstaking" + bbndatagen "github.com/babylonlabs-io/babylon/testutil/datagen" + queuecli "github.com/babylonlabs-io/staking-queue-client/client" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" + + "github.com/babylonlabs-io/staking-indexer/cmd/sid/cli" + "github.com/babylonlabs-io/staking-indexer/config" + "github.com/babylonlabs-io/staking-indexer/testutils" + "github.com/babylonlabs-io/staking-indexer/testutils/datagen" +) + +func TestBTCScanner(t *testing.T) { + n := 100 + tm := StartManagerWithNBlocks(t, n, uint64(n)) + defer tm.Stop() + + count, err := tm.BitcoindHandler.GetBlockCount() + require.NoError(t, err) + require.Equal(t, n, count) + + k := int(tm.VersionedParams.Versions[0].ConfirmationDepth) + + _ = tm.BitcoindHandler.GenerateBlocks(10) + + tm.WaitForNConfirmations(t, k) +} + +func TestQueueConsumer(t *testing.T) { + // create event consumer + queueCfg := config.DefaultQueueConfig() + queueConsumer, err := setupTestQueueConsumer(t, queueCfg) + require.NoError(t, err) + stakingChan, err := queueConsumer.StakingQueue.ReceiveMessages() + require.NoError(t, err) + + defer queueConsumer.Stop() + + n := 1 + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + stakingEventList := make([]*queuecli.ActiveStakingEvent, 0) + for i := 0; i < n; i++ { + stakingEvent := &queuecli.ActiveStakingEvent{ + EventType: queuecli.ActiveStakingEventType, + StakingTxHashHex: hex.EncodeToString(bbndatagen.GenRandomByteArray(r, 10)), + } + err = queueConsumer.PushStakingEvent(stakingEvent) + require.NoError(t, err) + stakingEventList = append(stakingEventList, stakingEvent) + } + + for i := 0; i < n; i++ { + stakingEventBytes := <-stakingChan + var receivedStakingEvent queuecli.ActiveStakingEvent + err = json.Unmarshal([]byte(stakingEventBytes.Body), &receivedStakingEvent) + require.NoError(t, err) + require.Equal(t, stakingEventList[i].StakingTxHashHex, receivedStakingEvent.StakingTxHashHex) + err = queueConsumer.StakingQueue.DeleteMessage(stakingEventBytes.Receipt) + require.NoError(t, err) + } +} + +// TestStakingLifeCycle covers the following life cycle +// 1. the staking tx is sent to BTC +// 2. the staking tx is parsed by the indexer +// 3. wait until the staking tx expires +// 4. the subsequent withdraw tx is sent to BTC +// 5. the withdraw tx is identified by the indexer and consumed by the queue +func TestStakingLifeCycle(t *testing.T) { + // ensure we have UTXOs + n := 101 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + // generate valid staking tx data + r := rand.New(rand.NewSource(time.Now().UnixNano())) + // TODO: test with multiple system parameters + sysParams := tm.VersionedParams.Versions[0] + k := uint64(sysParams.ConfirmationDepth) + + // build, send the staking tx and mine blocks + stakingTx, testStakingData, stakingInfo := tm.BuildStakingTx(t, r, sysParams) + stakingTxHash := stakingTx.TxHash() + tm.SendTxWithNConfirmations(t, stakingTx, int(k)) + + tm.CheckConfirmedInfoEvent(t, 100, 0) + + // check that the staking tx is already stored + _ = tm.WaitForStakingTxStored(t, stakingTxHash) + + // check the staking event is received by the queue + tm.CheckNextStakingEvent(t, stakingTxHash) + tm.CheckConfirmedInfoEvent(t, 102, uint64(testStakingData.StakingAmount)) + + // wait for the staking tx expires + if uint64(testStakingData.StakingTime) > k { + tm.BitcoindHandler.GenerateBlocks(int(uint64(testStakingData.StakingTime) - k)) + } + + // build and send withdraw tx and mine blocks + withdrawSpendInfo, err := stakingInfo.TimeLockPathSpendInfo() + require.NoError(t, err) + + storedStakingTx, err := tm.Si.GetStakingTxByHash(&stakingTxHash) + require.NoError(t, err) + require.NotNil(t, storedStakingTx) + fundingInfo := &testutils.FundingInfo{ + FundTxOutput: stakingTx.TxOut[storedStakingTx.StakingOutputIdx], + FundTxHash: stakingTxHash, + FundTxOutputIndex: storedStakingTx.StakingOutputIdx, + FundTxSpendInfo: withdrawSpendInfo, + LockTime: testStakingData.StakingTime, + Value: testStakingData.StakingAmount, + } + withdrawTx := testutils.BuildWithdrawTx( + t, + tm.WalletPrivKey, + []*testutils.FundingInfo{fundingInfo}, + regtestParams, + ) + tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) + + // check the withdraw event is received + tm.CheckNextWithdrawEvent(t, stakingTx.TxHash()) +} + +// TestWithdrawFromMultipleStakingTxs tests withdrawal from multiple staking tx in +// a single withdrawal tx +func TestWithdrawFromMultipleStakingTxs(t *testing.T) { + // ensure we have UTXOs + n := 110 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sysParams := tm.VersionedParams.Versions[0] + k := uint64(sysParams.ConfirmationDepth) + + // build, send the staking txs and mine blocks + stakingTx1, testStakingData1, stakingInfo1 := tm.BuildStakingTx(t, r, sysParams) + stakingTxHash1, err := tm.WalletClient.SendRawTransaction(stakingTx1, true) + stakingTx2, testStakingData2, stakingInfo2 := tm.BuildStakingTx(t, r, sysParams) + stakingTxHash2, err := tm.WalletClient.SendRawTransaction(stakingTx2, true) + + tm.BitcoindHandler.GenerateBlocks(int(k)) + + // check that the staking txs are already stored + _ = tm.WaitForStakingTxStored(t, *stakingTxHash1) + _ = tm.WaitForStakingTxStored(t, *stakingTxHash2) + + // wait for the staking tx expires + tm.BitcoindHandler.GenerateBlocks(int(uint64(sysParams.MaxStakingTime) - k)) + + // build and send withdraw tx and mine blocks + withdrawSpendInfo1, err := stakingInfo1.TimeLockPathSpendInfo() + require.NoError(t, err) + withdrawSpendInfo2, err := stakingInfo2.TimeLockPathSpendInfo() + require.NoError(t, err) + + storedStakingTx1, err := tm.Si.GetStakingTxByHash(stakingTxHash1) + require.NoError(t, err) + require.NotNil(t, storedStakingTx1) + storedStakingTx2, err := tm.Si.GetStakingTxByHash(stakingTxHash2) + require.NoError(t, err) + require.NotNil(t, storedStakingTx2) + + fundingInfo1 := &testutils.FundingInfo{ + FundTxOutput: stakingTx1.TxOut[storedStakingTx1.StakingOutputIdx], + FundTxHash: *stakingTxHash1, + FundTxOutputIndex: storedStakingTx1.StakingOutputIdx, + FundTxSpendInfo: withdrawSpendInfo1, + LockTime: testStakingData1.StakingTime, + Value: testStakingData1.StakingAmount, + } + fundingInfo2 := &testutils.FundingInfo{ + FundTxOutput: stakingTx2.TxOut[storedStakingTx2.StakingOutputIdx], + FundTxHash: *stakingTxHash2, + FundTxOutputIndex: storedStakingTx2.StakingOutputIdx, + FundTxSpendInfo: withdrawSpendInfo2, + LockTime: testStakingData2.StakingTime, + Value: testStakingData2.StakingAmount, + } + + withdrawTx := testutils.BuildWithdrawTx( + t, + tm.WalletPrivKey, + []*testutils.FundingInfo{fundingInfo1, fundingInfo2}, + regtestParams, + ) + tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) + + // check the withdraw events are received + tm.CheckNextWithdrawEvent(t, *stakingTxHash1) + tm.CheckNextWithdrawEvent(t, *stakingTxHash2) +} + +// TestWithdrawFromMultipleStakingTxs tests withdrawal from multiple unbonding tx in +// a single withdrawal tx +func TestWithdrawFromMultipleUnbondingTxs(t *testing.T) { + // ensure we have UTXOs + n := 110 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sysParams := tm.VersionedParams.Versions[0] + k := uint64(sysParams.ConfirmationDepth) + + // build, send the staking txs and mine blocks + stakingTx1, testStakingData1, stakingInfo1 := tm.BuildStakingTx(t, r, sysParams) + stakingTxHash1, err := tm.WalletClient.SendRawTransaction(stakingTx1, true) + require.NoError(t, err) + stakingTx2, testStakingData2, stakingInfo2 := tm.BuildStakingTx(t, r, sysParams) + stakingTxHash2, err := tm.WalletClient.SendRawTransaction(stakingTx2, true) + require.NoError(t, err) + + tm.BitcoindHandler.GenerateBlocks(int(k)) + + // check that the staking txs are already stored + storedStakingTx1 := tm.WaitForStakingTxStored(t, *stakingTxHash1) + storedStakingTx2 := tm.WaitForStakingTxStored(t, *stakingTxHash2) + + // build and send unbonding tx from the previous staking tx + unbondingTx1, unbondingInfo1 := testutils.BuildUnbondingTx( + t, sysParams, tm.WalletPrivKey, + testStakingData1.FinalityProviderKey, testStakingData1.StakingAmount, + stakingTxHash1, storedStakingTx1.StakingOutputIdx, stakingInfo1, stakingTx1, + getCovenantPrivKeys(t), regtestParams, + ) + unbondingTxHash1 := unbondingTx1.TxHash() + tm.SendTxWithNConfirmations(t, unbondingTx1, 1) + + unbondingTx2, unbondingInfo2 := testutils.BuildUnbondingTx( + t, sysParams, tm.WalletPrivKey, + testStakingData2.FinalityProviderKey, testStakingData2.StakingAmount, + stakingTxHash2, storedStakingTx2.StakingOutputIdx, stakingInfo2, stakingTx2, + getCovenantPrivKeys(t), regtestParams, + ) + unbondingTxHash2 := unbondingTx2.TxHash() + tm.SendTxWithNConfirmations(t, unbondingTx2, 1) + require.NoError(t, err) + + // wait for the unbonding tx expires + tm.BitcoindHandler.GenerateBlocks(int(sysParams.UnbondingTime)) + + // build and send withdraw tx and mine blocks + withdrawSpendInfo1, err := unbondingInfo1.TimeLockPathSpendInfo() + require.NoError(t, err) + withdrawSpendInfo2, err := unbondingInfo2.TimeLockPathSpendInfo() + require.NoError(t, err) + + fundingInfo1 := &testutils.FundingInfo{ + FundTxOutput: unbondingTx1.TxOut[0], + FundTxHash: unbondingTxHash1, + FundTxOutputIndex: 0, + FundTxSpendInfo: withdrawSpendInfo1, + LockTime: sysParams.UnbondingTime, + Value: btcutil.Amount(unbondingTx1.TxOut[0].Value), + } + fundingInfo2 := &testutils.FundingInfo{ + FundTxOutput: unbondingTx2.TxOut[0], + FundTxHash: unbondingTxHash2, + FundTxOutputIndex: 0, + FundTxSpendInfo: withdrawSpendInfo2, + LockTime: sysParams.UnbondingTime, + Value: btcutil.Amount(unbondingTx2.TxOut[0].Value), + } + + withdrawTx := testutils.BuildWithdrawTx( + t, + tm.WalletPrivKey, + []*testutils.FundingInfo{fundingInfo1, fundingInfo2}, + regtestParams, + ) + tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) + + // check the withdraw event is received + tm.CheckNextWithdrawEvent(t, *stakingTxHash1) + tm.CheckNextWithdrawEvent(t, *stakingTxHash2) + // consume unbonding events + tm.CheckNextUnbondingEvent(t, unbondingTxHash1) + tm.CheckNextUnbondingEvent(t, unbondingTxHash2) +} + +// TestWithdrawStakingAndUnbondingTxs tests withdrawal from staking and unbonding tx in +// a single withdrawal tx +func TestWithdrawStakingAndUnbondingTxs(t *testing.T) { + // ensure we have UTXOs + n := 110 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sysParams := tm.VersionedParams.Versions[0] + k := uint64(sysParams.ConfirmationDepth) + + // build, send the staking txs and mine blocks + stakingTx1, testStakingData1, stakingInfo1 := tm.BuildStakingTx(t, r, sysParams) + stakingTxHash1, err := tm.WalletClient.SendRawTransaction(stakingTx1, true) + require.NoError(t, err) + stakingTx2, testStakingData2, stakingInfo2 := tm.BuildStakingTx(t, r, sysParams) + stakingTxHash2, err := tm.WalletClient.SendRawTransaction(stakingTx2, true) + require.NoError(t, err) + + tm.BitcoindHandler.GenerateBlocks(int(k)) + + // check that the staking txs are already stored + storedStakingTx1 := tm.WaitForStakingTxStored(t, *stakingTxHash1) + storedStakingTx2 := tm.WaitForStakingTxStored(t, *stakingTxHash2) + + // build unbonding tx 1 from the previous staking tx 1 + unbondingTx1, unbondingInfo1 := testutils.BuildUnbondingTx( + t, sysParams, tm.WalletPrivKey, + testStakingData1.FinalityProviderKey, testStakingData1.StakingAmount, + stakingTxHash1, storedStakingTx1.StakingOutputIdx, stakingInfo1, stakingTx1, + getCovenantPrivKeys(t), regtestParams, + ) + unbondingTxHash1, err := tm.WalletClient.SendRawTransaction(unbondingTx1, true) + require.NoError(t, err) + + // wait for the staking tx expires + tm.BitcoindHandler.GenerateBlocks(int(uint64(sysParams.MaxStakingTime) - k)) + + // build withdrawal from unbonding tx 1 and staking tx 2 + withdrawSpendInfo1, err := unbondingInfo1.TimeLockPathSpendInfo() + require.NoError(t, err) + withdrawSpendInfo2, err := stakingInfo2.TimeLockPathSpendInfo() + require.NoError(t, err) + + fundingInfo1 := &testutils.FundingInfo{ + FundTxOutput: unbondingTx1.TxOut[0], + FundTxHash: *unbondingTxHash1, + FundTxOutputIndex: 0, + FundTxSpendInfo: withdrawSpendInfo1, + LockTime: sysParams.UnbondingTime, + Value: btcutil.Amount(unbondingTx1.TxOut[0].Value), + } + fundingInfo2 := &testutils.FundingInfo{ + FundTxOutput: stakingTx2.TxOut[storedStakingTx2.StakingOutputIdx], + FundTxHash: *stakingTxHash2, + FundTxOutputIndex: storedStakingTx2.StakingOutputIdx, + FundTxSpendInfo: withdrawSpendInfo2, + LockTime: testStakingData2.StakingTime, + Value: testStakingData2.StakingAmount, + } + + withdrawTx := testutils.BuildWithdrawTx( + t, + tm.WalletPrivKey, + []*testutils.FundingInfo{fundingInfo1, fundingInfo2}, + regtestParams, + ) + tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) + + // check the withdraw event is received + // this is expected to receive the withdrawal from + tm.CheckNextWithdrawEvent(t, *stakingTxHash2) + tm.CheckNextWithdrawEvent(t, *stakingTxHash1) + // consume unbonding event + tm.CheckNextUnbondingEvent(t, *unbondingTxHash1) +} + +func TestUnconfirmedTVL(t *testing.T) { + // ensure we have UTXOs + n := 101 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + tm.CheckNextUnconfirmedEvent(t, 0, 0) + + // generate valid staking tx data + r := rand.New(rand.NewSource(time.Now().UnixNano())) + // TODO: test with multiple system parameters + sysParams := tm.VersionedParams.Versions[0] + k := sysParams.ConfirmationDepth + + // build staking tx + stakingTx, testStakingData, stakingInfo := tm.BuildStakingTx(t, r, sysParams) + // send the staking tx and mine 1 block to trigger + // unconfirmed calculation + tm.SendTxWithNConfirmations(t, stakingTx, 1) + tm.CheckNextUnconfirmedEvent(t, 0, uint64(stakingInfo.StakingOutput.Value)) + + // confirm the staking tx + tm.BitcoindHandler.GenerateBlocks(int(k)) + tm.WaitForNConfirmations(t, int(k)) + tm.CheckNextStakingEvent(t, stakingTx.TxHash()) + tm.CheckNextUnconfirmedEvent(t, uint64(stakingInfo.StakingOutput.Value), uint64(stakingInfo.StakingOutput.Value)) + + // build and send unbonding tx from the previous staking tx + stakingTxHash := stakingTx.TxHash() + unbondingTx, _ := testutils.BuildUnbondingTx( + t, + sysParams, + tm.WalletPrivKey, + testStakingData.FinalityProviderKey, + testStakingData.StakingAmount, + &stakingTxHash, + 1, + stakingInfo, + stakingTx, + getCovenantPrivKeys(t), + regtestParams, + ) + tm.SendTxWithNConfirmations(t, unbondingTx, 1) + tm.CheckNextUnconfirmedEvent(t, uint64(stakingInfo.StakingOutput.Value), 0) + + // confirm the unbonding tx + tm.BitcoindHandler.GenerateBlocks(int(k)) + tm.WaitForNConfirmations(t, int(k)) + tm.CheckNextUnconfirmedEvent(t, 0, 0) + tm.CheckNextUnbondingEvent(t, unbondingTx.TxHash()) +} + +// TestIndexerRestart tests following cases upon restart +// 1. it restarts from a previous height before a staking tx is found. +// We expect the staking event to be replayed +// 2. it restarts exactly from the height it just processed. +// We expect the staking event not to be replayed +func TestIndexerRestart(t *testing.T) { + // ensure we have UTXOs + n := 101 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + // generate valid staking tx data + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sysParams := tm.VersionedParams.Versions[0] + k := sysParams.ConfirmationDepth + testStakingData := datagen.GenerateTestStakingData(t, r, sysParams) + stakingInfo, err := btcstaking.BuildV0IdentifiableStakingOutputs( + sysParams.Tag, + tm.WalletPrivKey.PubKey(), + testStakingData.FinalityProviderKey, + sysParams.CovenantPks, + sysParams.CovenantQuorum, + testStakingData.StakingTime, + testStakingData.StakingAmount, + regtestParams, + ) + require.NoError(t, err) + + // send the staking tx and mine blocks + require.NoError(t, err) + stakingTx, err := testutils.CreateTxFromOutputsAndSign( + tm.WalletClient, + []*wire.TxOut{stakingInfo.OpReturnOutput, stakingInfo.StakingOutput}, + 1000, + tm.MinerAddr, + ) + require.NoError(t, err) + stakingTxHash := stakingTx.TxHash() + tm.SendTxWithNConfirmations(t, stakingTx, int(k)) + + // check that the staking tx is already stored + _ = tm.WaitForStakingTxStored(t, stakingTxHash) + + // check the staking event is received by the queue + tm.CheckNextStakingEvent(t, stakingTxHash) + + // restart from a height before staking tx + restartedTm := ReStartFromHeight(t, tm, uint64(n)) + defer restartedTm.Stop() + + // check the staking event is replayed + restartedTm.CheckNextStakingEvent(t, stakingTxHash) + + // restart the testing manager again from last processed height + 1 + restartedTm2 := ReStartFromHeight(t, restartedTm, restartedTm.Si.GetStartHeight()) + defer restartedTm2.Stop() + + // no staking event should be replayed as + // the indexer starts from a higher height + restartedTm2.CheckNoStakingEvent(t) +} + +// TestStakingUnbondingLifeCycle covers the following life cycle +// 1. the staking tx is sent to BTC +// 2. the staking tx is parsed by the indexer +// 3. the subsequent unbonding tx is sent to BTC +// 4. the unbonding tx is identified by the indexer +// 5. the subsequent withdraw tx is sent to BTC +// 6. the withdraw tx is identified by the indexer +func TestStakingUnbondingLifeCycle(t *testing.T) { + // ensure we have UTXOs + n := 101 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + // generate valid staking tx data + // TODO: test with multiple system parameters + sysParams := tm.VersionedParams.Versions[0] + k := uint64(sysParams.ConfirmationDepth) + testStakingData := getTestStakingData(t) + stakingInfo, err := btcstaking.BuildV0IdentifiableStakingOutputs( + sysParams.Tag, + tm.WalletPrivKey.PubKey(), + testStakingData.FinalityProviderKey, + sysParams.CovenantPks, + sysParams.CovenantQuorum, + testStakingData.StakingTime, + testStakingData.StakingAmount, + regtestParams, + ) + require.NoError(t, err) + + // send the staking tx and mine blocks + require.NoError(t, err) + stakingTx, err := testutils.CreateTxFromOutputsAndSign( + tm.WalletClient, + []*wire.TxOut{stakingInfo.OpReturnOutput, stakingInfo.StakingOutput}, + 1000, + tm.MinerAddr, + ) + require.NoError(t, err) + stakingTxHash := stakingTx.TxHash() + tm.SendTxWithNConfirmations(t, stakingTx, int(k)) + + // check that the staking tx is already stored + _ = tm.WaitForStakingTxStored(t, stakingTxHash) + + // check the staking event is received by the queue + tm.CheckNextStakingEvent(t, stakingTxHash) + + // build and send unbonding tx from the previous staking tx + storedStakingTx, err := tm.Si.GetStakingTxByHash(&stakingTxHash) + require.NoError(t, err) + require.NotNil(t, storedStakingTx) + unbondingTx, _ := testutils.BuildUnbondingTx( + t, + sysParams, + tm.WalletPrivKey, + testStakingData.FinalityProviderKey, + testStakingData.StakingAmount, + &stakingTxHash, + storedStakingTx.StakingOutputIdx, + stakingInfo, + stakingTx, + getCovenantPrivKeys(t), + regtestParams, + ) + tm.SendTxWithNConfirmations(t, unbondingTx, int(k)) + + // check the unbonding tx is already stored + tm.WaitForUnbondingTxStored(t, unbondingTx.TxHash()) + + // check the unbonding event is received + tm.CheckNextUnbondingEvent(t, unbondingTx.TxHash()) + + // wait for the unbonding tx expires + if uint64(sysParams.UnbondingTime) > k { + tm.BitcoindHandler.GenerateBlocks(int(uint64(sysParams.UnbondingTime) - k)) + } + + // build and send withdraw tx from the unbonding tx + unbondingInfo, err := btcstaking.BuildUnbondingInfo( + tm.WalletPrivKey.PubKey(), + []*btcec.PublicKey{testStakingData.FinalityProviderKey}, + sysParams.CovenantPks, + sysParams.CovenantQuorum, + sysParams.UnbondingTime, + testStakingData.StakingAmount.MulF64(0.9), + regtestParams, + ) + require.NoError(t, err) + withdrawSpendInfo, err := unbondingInfo.TimeLockPathSpendInfo() + require.NoError(t, err) + fundingInfo := &testutils.FundingInfo{ + // unbonding tx only has one output + FundTxOutput: unbondingTx.TxOut[0], + FundTxHash: unbondingTx.TxHash(), + FundTxOutputIndex: 0, + FundTxSpendInfo: withdrawSpendInfo, + LockTime: sysParams.UnbondingTime, + Value: testStakingData.StakingAmount, + } + withdrawTx := testutils.BuildWithdrawTx( + t, + tm.WalletPrivKey, + []*testutils.FundingInfo{fundingInfo}, + regtestParams, + ) + tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) + + // wait until the indexer identifies the withdraw tx + tm.WaitForNConfirmations(t, int(k)) + + // check the withdraw event is consumed + tm.CheckNextWithdrawEvent(t, stakingTx.TxHash()) +} + +// TestTimeBasedCap tests the case where the time-based cap is applied +func TestTimeBasedCap(t *testing.T) { + // start from the height at which the time-based cap is effective + n := 110 + tm := StartManagerWithNBlocks(t, n, 100) + defer tm.Stop() + + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sysParams := tm.VersionedParams.Versions[1] + k := uint64(sysParams.ConfirmationDepth) + + // build and send staking tx which should not overflow + stakingTx, _, _ := tm.BuildStakingTx(t, r, sysParams) + tm.SendTxWithNConfirmations(t, stakingTx, int(k)) + storedTx := tm.WaitForStakingTxStored(t, stakingTx.TxHash()) + require.False(t, storedTx.IsOverflow) + + // generate blocks so that the height is out of the cap height + tm.BitcoindHandler.GenerateBlocks(20) + currentHeight, err := tm.BitcoindHandler.GetBlockCount() + require.NoError(t, err) + require.Greater(t, uint64(currentHeight), sysParams.CapHeight) + + // send another staking tx which should be overflow + stakingTx2, _, _ := tm.BuildStakingTx(t, r, sysParams) + tm.SendTxWithNConfirmations(t, stakingTx2, int(k)) + storedTx2 := tm.WaitForStakingTxStored(t, stakingTx2.TxHash()) + require.True(t, storedTx2.IsOverflow) +} + +func TestBtcHeaders(t *testing.T) { + r := rand.New(rand.NewSource(10)) + blocksPerRetarget := 2016 + + initBlocksQnt := r.Intn(15) + blocksPerRetarget + btcd, btcClient := StartBtcClientAndBtcHandler(t, initBlocksQnt) + + // from zero height + infos, err := cli.BtcHeaderInfoList(btcClient, 0, uint64(initBlocksQnt), true) + require.NoError(t, err) + require.Equal(t, len(infos), initBlocksQnt+1) + + generatedBlocksQnt := r.Intn(15) + 2 + btcd.GenerateBlocks(generatedBlocksQnt) + totalBlks := initBlocksQnt + generatedBlocksQnt + + // check from height with interval + fromBlockHeight := blocksPerRetarget - 1 + toBlockHeight := totalBlks - 2 + + infos, err = cli.BtcHeaderInfoList(btcClient, uint64(fromBlockHeight), uint64(toBlockHeight), true) + require.NoError(t, err) + require.Equal(t, len(infos), int(toBlockHeight-fromBlockHeight)+1) + require.EqualValues(t, infos[len(infos)-1].Height, uint64(toBlockHeight)) + + // from retarget block + infos, err = cli.BtcHeaderInfoList(btcClient, uint64(blocksPerRetarget), uint64(totalBlks), true) + require.NoError(t, err) + require.Equal(t, len(infos), int(totalBlks-blocksPerRetarget)+1) +} + +func getCovenantPrivKeys(t *testing.T) []*btcec.PrivateKey { + // private keys of the covenant committee which correspond to the public keys in test-params.json + covenantPrivKeysHex := []string{ + "6a2369c2c9f5cd3c4242834228acdc38b73e5b8930f5f4a9b69e6eaf557e60ed", + } + + privKeys := make([]*btcec.PrivateKey, len(covenantPrivKeysHex)) + for i, skHex := range covenantPrivKeysHex { + skBytes, err := hex.DecodeString(skHex) + require.NoError(t, err) + sk, _ := btcec.PrivKeyFromBytes(skBytes) + privKeys[i] = sk + } + + return privKeys +} + +func getTestStakingData( + t *testing.T, +) *datagen.TestStakingData { + stakerPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + fpPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + stakingAmount := btcutil.Amount(100000) + stakingTime := uint16(100) + + return &datagen.TestStakingData{ + StakerKey: stakerPrivKey.PubKey(), + FinalityProviderKey: fpPrivKey.PubKey(), + StakingAmount: stakingAmount, + StakingTime: stakingTime, + } +} diff --git a/itest/queue_setup.go b/itest/queue_setup.go new file mode 100644 index 0000000..657144e --- /dev/null +++ b/itest/queue_setup.go @@ -0,0 +1,61 @@ +package e2etest + +import ( + "fmt" + "strings" + "testing" + + "github.com/babylonlabs-io/staking-queue-client/client" + "github.com/babylonlabs-io/staking-queue-client/queuemngr" + "github.com/rabbitmq/amqp091-go" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/babylonlabs-io/staking-indexer/config" +) + +func setupTestQueueConsumer(t *testing.T, cfg *config.QueueConfig) (*queuemngr.QueueManager, error) { + amqpURI := fmt.Sprintf("amqp://%s:%s@%s", cfg.User, cfg.Password, cfg.Url) + conn, err := amqp091.Dial(amqpURI) + if err != nil { + t.Fatalf("failed to connect to RabbitMQ in test: %v", err) + } + defer conn.Close() + err = purgeQueues(conn, []string{ + client.ActiveStakingQueueName, + }) + if err != nil { + return nil, err + } + + // Start the actual queue processing in our codebase + + validQueueCfg, err := cfg.ToQueueClientConfig() + require.NoError(t, err) + queues, err := queuemngr.NewQueueManager(validQueueCfg, zap.NewNop()) + require.NoError(t, err) + + return queues, nil +} + +// purgeQueues purges all messages from the given list of queues. +func purgeQueues(conn *amqp091.Connection, queues []string) error { + ch, err := conn.Channel() + if err != nil { + return fmt.Errorf("failed to open a channel in test: %w", err) + } + defer ch.Close() + + for _, queue := range queues { + _, err := ch.QueuePurge(queue, false) + if err != nil { + if strings.Contains(err.Error(), "no queue") { + fmt.Printf("Queue '%s' not found, ignoring...\n", queue) + continue // Ignore this error and proceed with the next queue + } + return fmt.Errorf("failed to purge queue in test %s: %w", queue, err) + } + } + + return nil +} diff --git a/itest/scripts/start_rabbitmq.sh b/itest/scripts/start_rabbitmq.sh new file mode 100755 index 0000000..539d7b6 --- /dev/null +++ b/itest/scripts/start_rabbitmq.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Check if the RabbitMQ container is already running +RABBITMQ_CONTAINER_NAME="rabbitmq" +if [ $(docker ps -q -f name=^/${RABBITMQ_CONTAINER_NAME}$) ]; then + echo "RabbitMQ container already running. Skipping RabbitMQ startup." +else + echo "Starting RabbitMQ" + # Start RabbitMQ + docker compose up -d rabbitmq +fi diff --git a/itest/test-params.json b/itest/test-params.json new file mode 100644 index 0000000..3480ddc --- /dev/null +++ b/itest/test-params.json @@ -0,0 +1,38 @@ +{ + "versions": [ + { + "version": 0, + "activation_height": 100, + "staking_cap": 5000000000, + "tag": "01020304", + "covenant_pks": [ + "03cecdb3f9b99e0d67e806a9d1abd9d8c7811602dc7653bcb657a3faff29b76047" + ], + "covenant_quorum": 1, + "unbonding_time": 20, + "unbonding_fee": 1000, + "max_staking_amount": 300000, + "min_staking_amount": 3000, + "max_staking_time": 100, + "min_staking_time": 50, + "confirmation_depth": 10 + }, + { + "version": 1, + "activation_height": 110, + "cap_height": 120, + "tag": "01020304", + "covenant_pks": [ + "03cecdb3f9b99e0d67e806a9d1abd9d8c7811602dc7653bcb657a3faff29b76047" + ], + "covenant_quorum": 1, + "unbonding_time": 20, + "unbonding_fee": 1000, + "max_staking_amount": 300000, + "min_staking_amount": 3000, + "max_staking_time": 100, + "min_staking_time": 50, + "confirmation_depth": 10 + } + ] +} diff --git a/itest/test_manager.go b/itest/test_manager.go new file mode 100644 index 0000000..524f408 --- /dev/null +++ b/itest/test_manager.go @@ -0,0 +1,436 @@ +package e2etest + +import ( + "encoding/hex" + "encoding/json" + "math/rand" + "os" + "path/filepath" + "sync" + "testing" + "time" + + "github.com/babylonlabs-io/babylon/btcstaking" + "github.com/babylonlabs-io/networks/parameters/parser" + queuecli "github.com/babylonlabs-io/staking-queue-client/client" + "github.com/babylonlabs-io/staking-queue-client/queuemngr" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/rpcclient" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/signal" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/babylonlabs-io/staking-indexer/btcclient" + "github.com/babylonlabs-io/staking-indexer/btcscanner" + "github.com/babylonlabs-io/staking-indexer/config" + "github.com/babylonlabs-io/staking-indexer/indexer" + "github.com/babylonlabs-io/staking-indexer/indexerstore" + "github.com/babylonlabs-io/staking-indexer/log" + "github.com/babylonlabs-io/staking-indexer/params" + "github.com/babylonlabs-io/staking-indexer/server" + "github.com/babylonlabs-io/staking-indexer/testutils" + "github.com/babylonlabs-io/staking-indexer/testutils/datagen" +) + +type TestManager struct { + Config *config.Config + Db kvdb.Backend + Si *indexer.StakingIndexer + BS *btcscanner.BtcPoller + WalletPrivKey *btcec.PrivateKey + serverStopper *signal.Interceptor + wg *sync.WaitGroup + BitcoindHandler *BitcoindTestHandler + WalletClient *rpcclient.Client + MinerAddr btcutil.Address + DirPath string + QueueConsumer *queuemngr.QueueManager + StakingEventChan <-chan queuecli.QueueMessage + UnbondingEventChan <-chan queuecli.QueueMessage + WithdrawEventChan <-chan queuecli.QueueMessage + BtcInfoEventChan <-chan queuecli.QueueMessage + ConfirmedInfoEventChan <-chan queuecli.QueueMessage + VersionedParams *parser.ParsedGlobalParams +} + +// bitcoin params used for testing +var ( + regtestParams = &chaincfg.RegressionNetParams + eventuallyWaitTimeOut = 1 * time.Minute + eventuallyPollTime = 500 * time.Millisecond + testParamsPath = "test-params.json" + Passphrase = "pass" + WalletName = "test-wallet" +) + +func StartManagerWithNBlocks(t *testing.T, n int, startHeight uint64) *TestManager { + h := NewBitcoindHandler(t) + h.Start() + _ = h.CreateWallet(WalletName, Passphrase) + resp := h.GenerateBlocks(n) + + minerAddressDecoded, err := btcutil.DecodeAddress(resp.Address, regtestParams) + require.NoError(t, err) + + dirPath := filepath.Join(t.TempDir(), "sid", "e2etest") + err = os.MkdirAll(dirPath, 0750) + require.NoError(t, err) + + return StartWithBitcoinHandler(t, h, minerAddressDecoded, dirPath, startHeight) +} + +func StartBtcClientAndBtcHandler(t *testing.T, generateNBlocks int) (*BitcoindTestHandler, *btcclient.BTCClient) { + btcd := NewBitcoindHandler(t) + btcd.Start() + _ = btcd.CreateWallet(WalletName, Passphrase) + + resp := btcd.GenerateBlocks(generateNBlocks) + require.Equal(t, len(resp.Blocks), generateNBlocks) + + cfg := DefaultStakingIndexerConfig(t.TempDir()) + btcClient, err := btcclient.NewBTCClient( + cfg.BTCConfig, + zap.NewNop(), + ) + require.NoError(t, err) + + return btcd, btcClient +} + +func StartWithBitcoinHandler(t *testing.T, h *BitcoindTestHandler, minerAddress btcutil.Address, dirPath string, startHeight uint64) *TestManager { + cfg := DefaultStakingIndexerConfig(dirPath) + logger, err := log.NewRootLoggerWithFile(config.LogFile(dirPath), "debug") + require.NoError(t, err) + + rpcclient, err := rpcclient.New(cfg.BTCConfig.ToConnConfig(), nil) + require.NoError(t, err) + err = rpcclient.WalletPassphrase(Passphrase, 200) + require.NoError(t, err) + walletPrivKey, err := rpcclient.DumpPrivKey(minerAddress) + require.NoError(t, err) + + btcClient, err := btcclient.NewBTCClient( + cfg.BTCConfig, + logger, + ) + require.NoError(t, err) + + btcNotifier, err := btcscanner.NewBTCNotifier( + cfg.BTCConfig, + &cfg.BTCNetParams, + &btcscanner.EmptyHintCache{}, + ) + require.NoError(t, err) + + paramsRetriever, err := params.NewGlobalParamsRetriever(testParamsPath) + require.NoError(t, err) + versionedParams := paramsRetriever.VersionedParams() + require.NoError(t, err) + scanner, err := btcscanner.NewBTCScanner(versionedParams.Versions[0].ConfirmationDepth, logger, btcClient, btcNotifier) + require.NoError(t, err) + + // create event consumer + queueConsumer, err := setupTestQueueConsumer(t, cfg.QueueConfig) + require.NoError(t, err) + + stakingEventChan, err := queueConsumer.StakingQueue.ReceiveMessages() + require.NoError(t, err) + unbondingEventChan, err := queueConsumer.UnbondingQueue.ReceiveMessages() + require.NoError(t, err) + withdrawEventChan, err := queueConsumer.WithdrawQueue.ReceiveMessages() + require.NoError(t, err) + unconfirmedEventChan, err := queueConsumer.BtcInfoQueue.ReceiveMessages() + require.NoError(t, err) + confirmedInfoEventChan, err := queueConsumer.ConfirmedInfoQueue.ReceiveMessages() + require.NoError(t, err) + + db, err := cfg.DatabaseConfig.GetDbBackend() + require.NoError(t, err) + si, err := indexer.NewStakingIndexer(cfg, logger, queueConsumer, db, versionedParams, scanner) + require.NoError(t, err) + + interceptor, err := signal.Intercept() + require.NoError(t, err) + + service := server.NewStakingIndexerServer( + cfg, + queueConsumer, + db, + btcNotifier, + si, + logger, + interceptor, + ) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + err := service.RunUntilShutdown(startHeight) + require.NoError(t, err) + }() + // Wait for the server to start + time.Sleep(3 * time.Second) + + return &TestManager{ + Config: cfg, + Si: si, + BS: scanner, + serverStopper: &interceptor, + wg: &wg, + BitcoindHandler: h, + WalletClient: rpcclient, + WalletPrivKey: walletPrivKey.PrivKey, + MinerAddr: minerAddress, + DirPath: dirPath, + QueueConsumer: queueConsumer, + StakingEventChan: stakingEventChan, + UnbondingEventChan: unbondingEventChan, + WithdrawEventChan: withdrawEventChan, + BtcInfoEventChan: unconfirmedEventChan, + ConfirmedInfoEventChan: confirmedInfoEventChan, + VersionedParams: versionedParams, + } +} + +func (tm *TestManager) Stop() { + tm.serverStopper.RequestShutdown() + tm.wg.Wait() +} + +func ReStartFromHeight(t *testing.T, tm *TestManager, height uint64) *TestManager { + t.Logf("restarting the test manager from height %d", height) + tm.Stop() + + restartedTm := StartWithBitcoinHandler(t, tm.BitcoindHandler, tm.MinerAddr, tm.DirPath, height) + + t.Log("the test manager is restarted") + + return restartedTm +} + +func DefaultStakingIndexerConfig(homePath string) *config.Config { + defaultConfig := config.DefaultConfigWithHome(homePath) + + // enable emitting extra events for testing + defaultConfig.ExtraEventEnabled = true + + // both wallet and node are bitcoind + defaultConfig.BTCNetParams = *regtestParams + + bitcoindHost := "127.0.0.1:18443" + bitcoindUser := "user" + bitcoindPass := "pass" + + defaultConfig.BTCConfig.RPCHost = bitcoindHost + defaultConfig.BTCConfig.RPCUser = bitcoindUser + defaultConfig.BTCConfig.RPCPass = bitcoindPass + defaultConfig.BTCConfig.BlockPollingInterval = 1 * time.Second + defaultConfig.BTCConfig.TxPollingInterval = 1 * time.Second + + return defaultConfig +} + +func (tm *TestManager) BuildStakingTx( + t *testing.T, + r *rand.Rand, + params *parser.ParsedVersionedGlobalParams, +) (*wire.MsgTx, *datagen.TestStakingData, *btcstaking.IdentifiableStakingInfo) { + testStakingData := datagen.GenerateTestStakingData(t, r, params) + stakingInfo, err := btcstaking.BuildV0IdentifiableStakingOutputs( + params.Tag, + tm.WalletPrivKey.PubKey(), + testStakingData.FinalityProviderKey, + params.CovenantPks, + params.CovenantQuorum, + testStakingData.StakingTime, + testStakingData.StakingAmount, + regtestParams, + ) + require.NoError(t, err) + + stakingTx, err := testutils.CreateTxFromOutputsAndSign( + tm.WalletClient, + []*wire.TxOut{stakingInfo.OpReturnOutput, stakingInfo.StakingOutput}, + 1000, + tm.MinerAddr, + ) + require.NoError(t, err) + + return stakingTx, testStakingData, stakingInfo +} + +func (tm *TestManager) SendTxWithNConfirmations(t *testing.T, tx *wire.MsgTx, n int) { + txHash, err := tm.WalletClient.SendRawTransaction(tx, true) + require.NoError(t, err) + + require.Eventually(t, func() bool { + txFromMempool := retrieveTransactionFromMempool(t, tm.WalletClient, []*chainhash.Hash{txHash}) + return len(txFromMempool) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineNBlock(t, n) + require.Equal(t, 2, len(mBlock.Transactions)) + t.Logf("sent tx %s with %d confirmations", txHash.String(), n) +} + +func retrieveTransactionFromMempool(t *testing.T, client *rpcclient.Client, hashes []*chainhash.Hash) []*btcutil.Tx { + var txes []*btcutil.Tx + for _, txHash := range hashes { + tx, err := client.GetRawTransaction(txHash) + require.NoError(t, err) + txes = append(txes, tx) + } + return txes +} + +func (tm *TestManager) mineNBlock(t *testing.T, n int) *wire.MsgBlock { + resp := tm.BitcoindHandler.GenerateBlocks(n) + hash, err := chainhash.NewHashFromStr(resp.Blocks[0]) + require.NoError(t, err) + header, err := tm.WalletClient.GetBlock(hash) + require.NoError(t, err) + return header +} + +func (tm *TestManager) WaitForNConfirmations(t *testing.T, n int) { + currentHeight, err := tm.BitcoindHandler.GetBlockCount() + require.NoError(t, err) + require.Eventually(t, func() bool { + confirmedTip := tm.BS.LastConfirmedHeight() + return confirmedTip == uint64(currentHeight)-uint64(n)+1 + }, eventuallyWaitTimeOut, eventuallyPollTime) +} + +func (tm *TestManager) CheckNextStakingEvent(t *testing.T, stakingTxHash chainhash.Hash) { + stakingEventBytes := <-tm.StakingEventChan + var activeStakingEvent queuecli.ActiveStakingEvent + err := json.Unmarshal([]byte(stakingEventBytes.Body), &activeStakingEvent) + require.NoError(t, err) + + storedStakingTx, err := tm.Si.GetStakingTxByHash(&stakingTxHash) + require.NotNil(t, storedStakingTx) + require.NoError(t, err) + require.Equal(t, stakingTxHash.String(), activeStakingEvent.StakingTxHashHex) + require.Equal(t, storedStakingTx.Tx.TxHash().String(), activeStakingEvent.StakingTxHashHex) + require.Equal(t, uint64(storedStakingTx.StakingTime), activeStakingEvent.StakingTimeLock) + require.Equal(t, storedStakingTx.StakingValue, activeStakingEvent.StakingValue) + require.Equal(t, uint64(storedStakingTx.StakingOutputIdx), activeStakingEvent.StakingOutputIndex) + require.Equal(t, storedStakingTx.InclusionHeight, activeStakingEvent.StakingStartHeight) + require.Equal(t, storedStakingTx.IsOverflow, activeStakingEvent.IsOverflow) + require.Equal(t, hex.EncodeToString(schnorr.SerializePubKey(storedStakingTx.StakerPk)), activeStakingEvent.StakerPkHex) + require.Equal(t, hex.EncodeToString(schnorr.SerializePubKey(storedStakingTx.FinalityProviderPk)), activeStakingEvent.FinalityProviderPkHex) + + err = tm.QueueConsumer.StakingQueue.DeleteMessage(stakingEventBytes.Receipt) + require.NoError(t, err) +} + +func (tm *TestManager) CheckNoStakingEvent(t *testing.T) { + select { + case _, ok := <-tm.StakingEventChan: + require.False(t, ok) + default: + return + } +} + +func (tm *TestManager) CheckNextUnbondingEvent(t *testing.T, unbondingTxHash chainhash.Hash) { + unbondingEventBytes := <-tm.UnbondingEventChan + var unbondingEvent queuecli.UnbondingStakingEvent + err := json.Unmarshal([]byte(unbondingEventBytes.Body), &unbondingEvent) + require.NoError(t, err) + require.Equal(t, unbondingTxHash.String(), unbondingEvent.UnbondingTxHashHex) + + storedUnbondingTx, err := tm.Si.GetUnbondingTxByHash(&unbondingTxHash) + require.NoError(t, err) + require.NotNil(t, storedUnbondingTx) + require.Equal(t, storedUnbondingTx.Tx.TxHash().String(), unbondingEvent.UnbondingTxHashHex) + require.Equal(t, storedUnbondingTx.StakingTxHash.String(), unbondingEvent.StakingTxHashHex) + + err = tm.QueueConsumer.UnbondingQueue.DeleteMessage(unbondingEventBytes.Receipt) + require.NoError(t, err) +} + +func (tm *TestManager) CheckNextWithdrawEvent(t *testing.T, stakingTxHash chainhash.Hash) { + withdrawEventBytes := <-tm.WithdrawEventChan + var withdrawEvent queuecli.WithdrawStakingEvent + err := json.Unmarshal([]byte(withdrawEventBytes.Body), &withdrawEvent) + require.NoError(t, err) + require.Equal(t, stakingTxHash.String(), withdrawEvent.StakingTxHashHex) + + err = tm.QueueConsumer.WithdrawQueue.DeleteMessage(withdrawEventBytes.Receipt) + require.NoError(t, err) +} + +func (tm *TestManager) CheckConfirmedInfoEvent(t *testing.T, height, tvl uint64) { + var confirmedInfoEv queuecli.ConfirmedInfoEvent + + for { + confirmedInfoEventBytes := <-tm.ConfirmedInfoEventChan + err := tm.QueueConsumer.ConfirmedInfoQueue.DeleteMessage(confirmedInfoEventBytes.Receipt) + require.NoError(t, err) + err = json.Unmarshal([]byte(confirmedInfoEventBytes.Body), &confirmedInfoEv) + require.NoError(t, err) + if height != confirmedInfoEv.Height { + continue + } + require.Equal(t, confirmedInfoEv.Tvl, tvl) + return + } +} + +func (tm *TestManager) CheckNextUnconfirmedEvent(t *testing.T, confirmedTvl, totalTvl uint64) { + var btcInfoEvent queuecli.BtcInfoEvent + + for { + btcInfoEventBytes := <-tm.BtcInfoEventChan + err := tm.QueueConsumer.BtcInfoQueue.DeleteMessage(btcInfoEventBytes.Receipt) + require.NoError(t, err) + err = json.Unmarshal([]byte(btcInfoEventBytes.Body), &btcInfoEvent) + require.NoError(t, err) + if confirmedTvl != btcInfoEvent.ConfirmedTvl { + continue + } + if totalTvl != btcInfoEvent.UnconfirmedTvl { + continue + } + + return + } +} + +func (tm *TestManager) WaitForStakingTxStored(t *testing.T, txHash chainhash.Hash) *indexerstore.StoredStakingTransaction { + var storedTx indexerstore.StoredStakingTransaction + require.Eventually(t, func() bool { + storedStakingTx, err := tm.Si.GetStakingTxByHash(&txHash) + if err != nil || storedStakingTx == nil { + return false + } + storedTx = *storedStakingTx + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + + require.Equal(t, txHash.String(), storedTx.Tx.TxHash().String()) + + return &storedTx +} + +func (tm *TestManager) WaitForUnbondingTxStored(t *testing.T, txHash chainhash.Hash) { + var storedTx indexerstore.StoredUnbondingTransaction + require.Eventually(t, func() bool { + storedUnbondingTx, err := tm.Si.GetUnbondingTxByHash(&txHash) + if err != nil || storedUnbondingTx == nil { + return false + } + storedTx = *storedUnbondingTx + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + + require.Equal(t, txHash.String(), storedTx.Tx.TxHash().String()) +} From 7370d720c53641fc22c16d9cc9859b8678860810 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 13:45:33 +0530 Subject: [PATCH 02/32] rm itest --- itest/bitcoind_node_setup.go | 111 ----- itest/containers/config.go | 24 -- itest/containers/containers.go | 186 --------- itest/e2e_test.go | 703 -------------------------------- itest/queue_setup.go | 61 --- itest/scripts/start_rabbitmq.sh | 11 - itest/test-params.json | 38 -- itest/test_manager.go | 436 -------------------- 8 files changed, 1570 deletions(-) delete mode 100644 itest/bitcoind_node_setup.go delete mode 100644 itest/containers/config.go delete mode 100644 itest/containers/containers.go delete mode 100644 itest/e2e_test.go delete mode 100644 itest/queue_setup.go delete mode 100755 itest/scripts/start_rabbitmq.sh delete mode 100644 itest/test-params.json delete mode 100644 itest/test_manager.go diff --git a/itest/bitcoind_node_setup.go b/itest/bitcoind_node_setup.go deleted file mode 100644 index de90e1c..0000000 --- a/itest/bitcoind_node_setup.go +++ /dev/null @@ -1,111 +0,0 @@ -package e2etest - -import ( - "encoding/json" - "fmt" - "os" - "strconv" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/babylonlabs-io/staking-indexer/itest/containers" -) - -var ( - startTimeout = 30 * time.Second -) - -type CreateWalletResponse struct { - Name string `json:"name"` - Warning string `json:"warning"` -} - -type GenerateBlockResponse struct { - // address of the recipient of rewards - Address string `json:"address"` - // blocks generated - Blocks []string `json:"blocks"` -} - -type BitcoindTestHandler struct { - t *testing.T - m *containers.Manager -} - -func NewBitcoindHandler(t *testing.T) *BitcoindTestHandler { - m, err := containers.NewManager() - require.NoError(t, err) - return &BitcoindTestHandler{ - t: t, - m: m, - } -} - -func (h *BitcoindTestHandler) Start() { - tempPath, err := os.MkdirTemp("", "bitcoind-staker-test-*") - require.NoError(h.t, err) - - h.t.Cleanup(func() { - _ = os.RemoveAll(tempPath) - }) - - _, err = h.m.RunBitcoindResource(tempPath) - require.NoError(h.t, err) - - h.t.Cleanup(func() { - _ = h.m.ClearResources() - }) - - require.Eventually(h.t, func() bool { - _, err := h.GetBlockCount() - h.t.Logf("failed to get block count: %v", err) - return err == nil - }, startTimeout, 500*time.Millisecond, "bitcoind did not start") - -} - -func (h *BitcoindTestHandler) GetBlockCount() (int, error) { - buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"getblockcount"}) - if err != nil { - return 0, err - } - - buffStr := buff.String() - - parsedBuffStr := strings.TrimSuffix(buffStr, "\n") - - num, err := strconv.Atoi(parsedBuffStr) - if err != nil { - return 0, err - } - - return num, nil -} - -func (h *BitcoindTestHandler) CreateWallet(walletName string, passphrase string) *CreateWalletResponse { - // last false on the list will create legacy wallet. This is needed, as currently - // we are signing all taproot transactions by dumping the private key and signing it - // on app level. Descriptor wallets do not allow dumping private keys. - buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"createwallet", walletName, "false", "false", passphrase, "false", "false"}) - require.NoError(h.t, err) - - var response CreateWalletResponse - err = json.Unmarshal(buff.Bytes(), &response) - require.NoError(h.t, err) - - return &response -} - -func (h *BitcoindTestHandler) GenerateBlocks(count int) *GenerateBlockResponse { - buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"-generate", fmt.Sprintf("%d", count)}) - require.NoError(h.t, err) - - var response GenerateBlockResponse - err = json.Unmarshal(buff.Bytes(), &response) - require.NoError(h.t, err) - - return &response -} diff --git a/itest/containers/config.go b/itest/containers/config.go deleted file mode 100644 index 2521e78..0000000 --- a/itest/containers/config.go +++ /dev/null @@ -1,24 +0,0 @@ -package containers - -// ImageConfig contains all images and their respective tags -// needed for running e2e tests. -type ImageConfig struct { - BitcoindRepository string - BitcoindVersion string -} - -//nolint:deadcode -const ( - dockerBitcoindRepository = "lncm/bitcoind" - dockerBitcoindVersionTag = "v24.0.1" -) - -// NewImageConfig returns ImageConfig needed for running e2e test. -func NewImageConfig() ImageConfig { - config := ImageConfig{ - BitcoindRepository: dockerBitcoindRepository, - BitcoindVersion: dockerBitcoindVersionTag, - } - return config - -} diff --git a/itest/containers/containers.go b/itest/containers/containers.go deleted file mode 100644 index 2e4100b..0000000 --- a/itest/containers/containers.go +++ /dev/null @@ -1,186 +0,0 @@ -package containers - -import ( - "bytes" - "context" - "fmt" - "regexp" - "testing" - "time" - - "github.com/ory/dockertest/v3" - "github.com/ory/dockertest/v3/docker" - "github.com/stretchr/testify/require" -) - -const ( - bitcoindContainerName = "bitcoind-test" -) - -var errRegex = regexp.MustCompile(`(E|e)rror`) - -// Manager is a wrapper around all Docker instances, and the Docker API. -// It provides utilities to run and interact with all Docker containers used within e2e testing. -type Manager struct { - cfg ImageConfig - pool *dockertest.Pool - resources map[string]*dockertest.Resource -} - -// NewManager creates a new Manager instance and initializes -// all Docker specific utilities. Returns an error if initialization fails. -func NewManager() (docker *Manager, err error) { - docker = &Manager{ - cfg: NewImageConfig(), - resources: make(map[string]*dockertest.Resource), - } - docker.pool, err = dockertest.NewPool("") - if err != nil { - return nil, err - } - return docker, nil -} - -func (m *Manager) ExecBitcoindCliCmd(t *testing.T, command []string) (bytes.Buffer, bytes.Buffer, error) { - // this is currently hardcoded, as it will be the same for all tests - cmd := []string{"bitcoin-cli", "-chain=regtest", "-rpcuser=user", "-rpcpassword=pass"} - cmd = append(cmd, command...) - return m.ExecCmd(t, bitcoindContainerName, cmd) -} - -// ExecCmd executes command by running it on the given container. -// It word for word `error` in output to discern between error and regular output. -// It retures stdout and stderr as bytes.Buffer and an error if the command fails. -func (m *Manager) ExecCmd(t *testing.T, containerName string, command []string) (bytes.Buffer, bytes.Buffer, error) { - if _, ok := m.resources[containerName]; !ok { - return bytes.Buffer{}, bytes.Buffer{}, fmt.Errorf("no resource %s found", containerName) - } - containerId := m.resources[containerName].Container.ID - - var ( - outBuf bytes.Buffer - errBuf bytes.Buffer - ) - - timeout := 20 * time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - t.Logf("\n\nRunning: \"%s\"", command) - - // We use the `require.Eventually` function because it is only allowed to do one transaction per block without - // sequence numbers. For simplicity, we avoid keeping track of the sequence number and just use the `require.Eventually`. - require.Eventually( - t, - func() bool { - exec, err := m.pool.Client.CreateExec(docker.CreateExecOptions{ - Context: ctx, - AttachStdout: true, - AttachStderr: true, - Container: containerId, - User: "root", - Cmd: command, - }) - - if err != nil { - t.Logf("failed to create exec: %v", err) - return false - } - - err = m.pool.Client.StartExec(exec.ID, docker.StartExecOptions{ - Context: ctx, - Detach: false, - OutputStream: &outBuf, - ErrorStream: &errBuf, - }) - if err != nil { - t.Logf("failed to start exec: %v", err) - return false - } - - errBufString := errBuf.String() - // Note that this does not match all errors. - // This only works if CLI outputs "Error" or "error" - // to stderr. - if errRegex.MatchString(errBufString) { - t.Log("\nstderr:") - t.Log(errBufString) - - t.Log("\nstdout:") - t.Log(outBuf.String()) - return false - } - - return true - }, - timeout, - 500*time.Millisecond, - "command failed", - ) - - return outBuf, errBuf, nil -} - -func (m *Manager) RunBitcoindResource( - bitcoindCfgPath string, -) (*dockertest.Resource, error) { - bitcoindResource, err := m.pool.RunWithOptions( - &dockertest.RunOptions{ - Name: bitcoindContainerName, - Repository: m.cfg.BitcoindRepository, - Tag: m.cfg.BitcoindVersion, - User: "root:root", - Mounts: []string{ - fmt.Sprintf("%s/:/data/.bitcoin", bitcoindCfgPath), - }, - ExposedPorts: []string{ - "8332", - "8333", - "28332", - "28333", - "18443", - "18444", - }, - PortBindings: map[docker.Port][]docker.PortBinding{ - "8332/tcp": {{HostIP: "", HostPort: "8332"}}, - "8333/tcp": {{HostIP: "", HostPort: "8333"}}, - "28332/tcp": {{HostIP: "", HostPort: "28332"}}, - "28333/tcp": {{HostIP: "", HostPort: "28333"}}, - "18443/tcp": {{HostIP: "", HostPort: "18443"}}, - "18444/tcp": {{HostIP: "", HostPort: "18444"}}, - }, - Cmd: []string{ - "-regtest", - "-txindex", - "-rpcuser=user", - "-rpcpassword=pass", - "-rpcallowip=0.0.0.0/0", - "-rpcbind=0.0.0.0", - }, - }, - noRestart, - ) - if err != nil { - return nil, err - } - m.resources[bitcoindContainerName] = bitcoindResource - return bitcoindResource, nil -} - -// ClearResources removes all outstanding Docker resources created by the Manager. -func (m *Manager) ClearResources() error { - for _, resource := range m.resources { - if err := m.pool.Purge(resource); err != nil { - return err - } - } - - return nil -} - -func noRestart(config *docker.HostConfig) { - // in this case we don't want the nodes to restart on failure - config.RestartPolicy = docker.RestartPolicy{ - Name: "no", - } -} diff --git a/itest/e2e_test.go b/itest/e2e_test.go deleted file mode 100644 index f37b4b7..0000000 --- a/itest/e2e_test.go +++ /dev/null @@ -1,703 +0,0 @@ -//go:build e2e -// +build e2e - -package e2etest - -import ( - "encoding/hex" - "encoding/json" - "math/rand" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/btcstaking" - bbndatagen "github.com/babylonlabs-io/babylon/testutil/datagen" - queuecli "github.com/babylonlabs-io/staking-queue-client/client" - "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/wire" - "github.com/stretchr/testify/require" - - "github.com/babylonlabs-io/staking-indexer/cmd/sid/cli" - "github.com/babylonlabs-io/staking-indexer/config" - "github.com/babylonlabs-io/staking-indexer/testutils" - "github.com/babylonlabs-io/staking-indexer/testutils/datagen" -) - -func TestBTCScanner(t *testing.T) { - n := 100 - tm := StartManagerWithNBlocks(t, n, uint64(n)) - defer tm.Stop() - - count, err := tm.BitcoindHandler.GetBlockCount() - require.NoError(t, err) - require.Equal(t, n, count) - - k := int(tm.VersionedParams.Versions[0].ConfirmationDepth) - - _ = tm.BitcoindHandler.GenerateBlocks(10) - - tm.WaitForNConfirmations(t, k) -} - -func TestQueueConsumer(t *testing.T) { - // create event consumer - queueCfg := config.DefaultQueueConfig() - queueConsumer, err := setupTestQueueConsumer(t, queueCfg) - require.NoError(t, err) - stakingChan, err := queueConsumer.StakingQueue.ReceiveMessages() - require.NoError(t, err) - - defer queueConsumer.Stop() - - n := 1 - r := rand.New(rand.NewSource(time.Now().UnixNano())) - - stakingEventList := make([]*queuecli.ActiveStakingEvent, 0) - for i := 0; i < n; i++ { - stakingEvent := &queuecli.ActiveStakingEvent{ - EventType: queuecli.ActiveStakingEventType, - StakingTxHashHex: hex.EncodeToString(bbndatagen.GenRandomByteArray(r, 10)), - } - err = queueConsumer.PushStakingEvent(stakingEvent) - require.NoError(t, err) - stakingEventList = append(stakingEventList, stakingEvent) - } - - for i := 0; i < n; i++ { - stakingEventBytes := <-stakingChan - var receivedStakingEvent queuecli.ActiveStakingEvent - err = json.Unmarshal([]byte(stakingEventBytes.Body), &receivedStakingEvent) - require.NoError(t, err) - require.Equal(t, stakingEventList[i].StakingTxHashHex, receivedStakingEvent.StakingTxHashHex) - err = queueConsumer.StakingQueue.DeleteMessage(stakingEventBytes.Receipt) - require.NoError(t, err) - } -} - -// TestStakingLifeCycle covers the following life cycle -// 1. the staking tx is sent to BTC -// 2. the staking tx is parsed by the indexer -// 3. wait until the staking tx expires -// 4. the subsequent withdraw tx is sent to BTC -// 5. the withdraw tx is identified by the indexer and consumed by the queue -func TestStakingLifeCycle(t *testing.T) { - // ensure we have UTXOs - n := 101 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - // generate valid staking tx data - r := rand.New(rand.NewSource(time.Now().UnixNano())) - // TODO: test with multiple system parameters - sysParams := tm.VersionedParams.Versions[0] - k := uint64(sysParams.ConfirmationDepth) - - // build, send the staking tx and mine blocks - stakingTx, testStakingData, stakingInfo := tm.BuildStakingTx(t, r, sysParams) - stakingTxHash := stakingTx.TxHash() - tm.SendTxWithNConfirmations(t, stakingTx, int(k)) - - tm.CheckConfirmedInfoEvent(t, 100, 0) - - // check that the staking tx is already stored - _ = tm.WaitForStakingTxStored(t, stakingTxHash) - - // check the staking event is received by the queue - tm.CheckNextStakingEvent(t, stakingTxHash) - tm.CheckConfirmedInfoEvent(t, 102, uint64(testStakingData.StakingAmount)) - - // wait for the staking tx expires - if uint64(testStakingData.StakingTime) > k { - tm.BitcoindHandler.GenerateBlocks(int(uint64(testStakingData.StakingTime) - k)) - } - - // build and send withdraw tx and mine blocks - withdrawSpendInfo, err := stakingInfo.TimeLockPathSpendInfo() - require.NoError(t, err) - - storedStakingTx, err := tm.Si.GetStakingTxByHash(&stakingTxHash) - require.NoError(t, err) - require.NotNil(t, storedStakingTx) - fundingInfo := &testutils.FundingInfo{ - FundTxOutput: stakingTx.TxOut[storedStakingTx.StakingOutputIdx], - FundTxHash: stakingTxHash, - FundTxOutputIndex: storedStakingTx.StakingOutputIdx, - FundTxSpendInfo: withdrawSpendInfo, - LockTime: testStakingData.StakingTime, - Value: testStakingData.StakingAmount, - } - withdrawTx := testutils.BuildWithdrawTx( - t, - tm.WalletPrivKey, - []*testutils.FundingInfo{fundingInfo}, - regtestParams, - ) - tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) - - // check the withdraw event is received - tm.CheckNextWithdrawEvent(t, stakingTx.TxHash()) -} - -// TestWithdrawFromMultipleStakingTxs tests withdrawal from multiple staking tx in -// a single withdrawal tx -func TestWithdrawFromMultipleStakingTxs(t *testing.T) { - // ensure we have UTXOs - n := 110 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - r := rand.New(rand.NewSource(time.Now().UnixNano())) - sysParams := tm.VersionedParams.Versions[0] - k := uint64(sysParams.ConfirmationDepth) - - // build, send the staking txs and mine blocks - stakingTx1, testStakingData1, stakingInfo1 := tm.BuildStakingTx(t, r, sysParams) - stakingTxHash1, err := tm.WalletClient.SendRawTransaction(stakingTx1, true) - stakingTx2, testStakingData2, stakingInfo2 := tm.BuildStakingTx(t, r, sysParams) - stakingTxHash2, err := tm.WalletClient.SendRawTransaction(stakingTx2, true) - - tm.BitcoindHandler.GenerateBlocks(int(k)) - - // check that the staking txs are already stored - _ = tm.WaitForStakingTxStored(t, *stakingTxHash1) - _ = tm.WaitForStakingTxStored(t, *stakingTxHash2) - - // wait for the staking tx expires - tm.BitcoindHandler.GenerateBlocks(int(uint64(sysParams.MaxStakingTime) - k)) - - // build and send withdraw tx and mine blocks - withdrawSpendInfo1, err := stakingInfo1.TimeLockPathSpendInfo() - require.NoError(t, err) - withdrawSpendInfo2, err := stakingInfo2.TimeLockPathSpendInfo() - require.NoError(t, err) - - storedStakingTx1, err := tm.Si.GetStakingTxByHash(stakingTxHash1) - require.NoError(t, err) - require.NotNil(t, storedStakingTx1) - storedStakingTx2, err := tm.Si.GetStakingTxByHash(stakingTxHash2) - require.NoError(t, err) - require.NotNil(t, storedStakingTx2) - - fundingInfo1 := &testutils.FundingInfo{ - FundTxOutput: stakingTx1.TxOut[storedStakingTx1.StakingOutputIdx], - FundTxHash: *stakingTxHash1, - FundTxOutputIndex: storedStakingTx1.StakingOutputIdx, - FundTxSpendInfo: withdrawSpendInfo1, - LockTime: testStakingData1.StakingTime, - Value: testStakingData1.StakingAmount, - } - fundingInfo2 := &testutils.FundingInfo{ - FundTxOutput: stakingTx2.TxOut[storedStakingTx2.StakingOutputIdx], - FundTxHash: *stakingTxHash2, - FundTxOutputIndex: storedStakingTx2.StakingOutputIdx, - FundTxSpendInfo: withdrawSpendInfo2, - LockTime: testStakingData2.StakingTime, - Value: testStakingData2.StakingAmount, - } - - withdrawTx := testutils.BuildWithdrawTx( - t, - tm.WalletPrivKey, - []*testutils.FundingInfo{fundingInfo1, fundingInfo2}, - regtestParams, - ) - tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) - - // check the withdraw events are received - tm.CheckNextWithdrawEvent(t, *stakingTxHash1) - tm.CheckNextWithdrawEvent(t, *stakingTxHash2) -} - -// TestWithdrawFromMultipleStakingTxs tests withdrawal from multiple unbonding tx in -// a single withdrawal tx -func TestWithdrawFromMultipleUnbondingTxs(t *testing.T) { - // ensure we have UTXOs - n := 110 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - r := rand.New(rand.NewSource(time.Now().UnixNano())) - sysParams := tm.VersionedParams.Versions[0] - k := uint64(sysParams.ConfirmationDepth) - - // build, send the staking txs and mine blocks - stakingTx1, testStakingData1, stakingInfo1 := tm.BuildStakingTx(t, r, sysParams) - stakingTxHash1, err := tm.WalletClient.SendRawTransaction(stakingTx1, true) - require.NoError(t, err) - stakingTx2, testStakingData2, stakingInfo2 := tm.BuildStakingTx(t, r, sysParams) - stakingTxHash2, err := tm.WalletClient.SendRawTransaction(stakingTx2, true) - require.NoError(t, err) - - tm.BitcoindHandler.GenerateBlocks(int(k)) - - // check that the staking txs are already stored - storedStakingTx1 := tm.WaitForStakingTxStored(t, *stakingTxHash1) - storedStakingTx2 := tm.WaitForStakingTxStored(t, *stakingTxHash2) - - // build and send unbonding tx from the previous staking tx - unbondingTx1, unbondingInfo1 := testutils.BuildUnbondingTx( - t, sysParams, tm.WalletPrivKey, - testStakingData1.FinalityProviderKey, testStakingData1.StakingAmount, - stakingTxHash1, storedStakingTx1.StakingOutputIdx, stakingInfo1, stakingTx1, - getCovenantPrivKeys(t), regtestParams, - ) - unbondingTxHash1 := unbondingTx1.TxHash() - tm.SendTxWithNConfirmations(t, unbondingTx1, 1) - - unbondingTx2, unbondingInfo2 := testutils.BuildUnbondingTx( - t, sysParams, tm.WalletPrivKey, - testStakingData2.FinalityProviderKey, testStakingData2.StakingAmount, - stakingTxHash2, storedStakingTx2.StakingOutputIdx, stakingInfo2, stakingTx2, - getCovenantPrivKeys(t), regtestParams, - ) - unbondingTxHash2 := unbondingTx2.TxHash() - tm.SendTxWithNConfirmations(t, unbondingTx2, 1) - require.NoError(t, err) - - // wait for the unbonding tx expires - tm.BitcoindHandler.GenerateBlocks(int(sysParams.UnbondingTime)) - - // build and send withdraw tx and mine blocks - withdrawSpendInfo1, err := unbondingInfo1.TimeLockPathSpendInfo() - require.NoError(t, err) - withdrawSpendInfo2, err := unbondingInfo2.TimeLockPathSpendInfo() - require.NoError(t, err) - - fundingInfo1 := &testutils.FundingInfo{ - FundTxOutput: unbondingTx1.TxOut[0], - FundTxHash: unbondingTxHash1, - FundTxOutputIndex: 0, - FundTxSpendInfo: withdrawSpendInfo1, - LockTime: sysParams.UnbondingTime, - Value: btcutil.Amount(unbondingTx1.TxOut[0].Value), - } - fundingInfo2 := &testutils.FundingInfo{ - FundTxOutput: unbondingTx2.TxOut[0], - FundTxHash: unbondingTxHash2, - FundTxOutputIndex: 0, - FundTxSpendInfo: withdrawSpendInfo2, - LockTime: sysParams.UnbondingTime, - Value: btcutil.Amount(unbondingTx2.TxOut[0].Value), - } - - withdrawTx := testutils.BuildWithdrawTx( - t, - tm.WalletPrivKey, - []*testutils.FundingInfo{fundingInfo1, fundingInfo2}, - regtestParams, - ) - tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) - - // check the withdraw event is received - tm.CheckNextWithdrawEvent(t, *stakingTxHash1) - tm.CheckNextWithdrawEvent(t, *stakingTxHash2) - // consume unbonding events - tm.CheckNextUnbondingEvent(t, unbondingTxHash1) - tm.CheckNextUnbondingEvent(t, unbondingTxHash2) -} - -// TestWithdrawStakingAndUnbondingTxs tests withdrawal from staking and unbonding tx in -// a single withdrawal tx -func TestWithdrawStakingAndUnbondingTxs(t *testing.T) { - // ensure we have UTXOs - n := 110 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - r := rand.New(rand.NewSource(time.Now().UnixNano())) - sysParams := tm.VersionedParams.Versions[0] - k := uint64(sysParams.ConfirmationDepth) - - // build, send the staking txs and mine blocks - stakingTx1, testStakingData1, stakingInfo1 := tm.BuildStakingTx(t, r, sysParams) - stakingTxHash1, err := tm.WalletClient.SendRawTransaction(stakingTx1, true) - require.NoError(t, err) - stakingTx2, testStakingData2, stakingInfo2 := tm.BuildStakingTx(t, r, sysParams) - stakingTxHash2, err := tm.WalletClient.SendRawTransaction(stakingTx2, true) - require.NoError(t, err) - - tm.BitcoindHandler.GenerateBlocks(int(k)) - - // check that the staking txs are already stored - storedStakingTx1 := tm.WaitForStakingTxStored(t, *stakingTxHash1) - storedStakingTx2 := tm.WaitForStakingTxStored(t, *stakingTxHash2) - - // build unbonding tx 1 from the previous staking tx 1 - unbondingTx1, unbondingInfo1 := testutils.BuildUnbondingTx( - t, sysParams, tm.WalletPrivKey, - testStakingData1.FinalityProviderKey, testStakingData1.StakingAmount, - stakingTxHash1, storedStakingTx1.StakingOutputIdx, stakingInfo1, stakingTx1, - getCovenantPrivKeys(t), regtestParams, - ) - unbondingTxHash1, err := tm.WalletClient.SendRawTransaction(unbondingTx1, true) - require.NoError(t, err) - - // wait for the staking tx expires - tm.BitcoindHandler.GenerateBlocks(int(uint64(sysParams.MaxStakingTime) - k)) - - // build withdrawal from unbonding tx 1 and staking tx 2 - withdrawSpendInfo1, err := unbondingInfo1.TimeLockPathSpendInfo() - require.NoError(t, err) - withdrawSpendInfo2, err := stakingInfo2.TimeLockPathSpendInfo() - require.NoError(t, err) - - fundingInfo1 := &testutils.FundingInfo{ - FundTxOutput: unbondingTx1.TxOut[0], - FundTxHash: *unbondingTxHash1, - FundTxOutputIndex: 0, - FundTxSpendInfo: withdrawSpendInfo1, - LockTime: sysParams.UnbondingTime, - Value: btcutil.Amount(unbondingTx1.TxOut[0].Value), - } - fundingInfo2 := &testutils.FundingInfo{ - FundTxOutput: stakingTx2.TxOut[storedStakingTx2.StakingOutputIdx], - FundTxHash: *stakingTxHash2, - FundTxOutputIndex: storedStakingTx2.StakingOutputIdx, - FundTxSpendInfo: withdrawSpendInfo2, - LockTime: testStakingData2.StakingTime, - Value: testStakingData2.StakingAmount, - } - - withdrawTx := testutils.BuildWithdrawTx( - t, - tm.WalletPrivKey, - []*testutils.FundingInfo{fundingInfo1, fundingInfo2}, - regtestParams, - ) - tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) - - // check the withdraw event is received - // this is expected to receive the withdrawal from - tm.CheckNextWithdrawEvent(t, *stakingTxHash2) - tm.CheckNextWithdrawEvent(t, *stakingTxHash1) - // consume unbonding event - tm.CheckNextUnbondingEvent(t, *unbondingTxHash1) -} - -func TestUnconfirmedTVL(t *testing.T) { - // ensure we have UTXOs - n := 101 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - tm.CheckNextUnconfirmedEvent(t, 0, 0) - - // generate valid staking tx data - r := rand.New(rand.NewSource(time.Now().UnixNano())) - // TODO: test with multiple system parameters - sysParams := tm.VersionedParams.Versions[0] - k := sysParams.ConfirmationDepth - - // build staking tx - stakingTx, testStakingData, stakingInfo := tm.BuildStakingTx(t, r, sysParams) - // send the staking tx and mine 1 block to trigger - // unconfirmed calculation - tm.SendTxWithNConfirmations(t, stakingTx, 1) - tm.CheckNextUnconfirmedEvent(t, 0, uint64(stakingInfo.StakingOutput.Value)) - - // confirm the staking tx - tm.BitcoindHandler.GenerateBlocks(int(k)) - tm.WaitForNConfirmations(t, int(k)) - tm.CheckNextStakingEvent(t, stakingTx.TxHash()) - tm.CheckNextUnconfirmedEvent(t, uint64(stakingInfo.StakingOutput.Value), uint64(stakingInfo.StakingOutput.Value)) - - // build and send unbonding tx from the previous staking tx - stakingTxHash := stakingTx.TxHash() - unbondingTx, _ := testutils.BuildUnbondingTx( - t, - sysParams, - tm.WalletPrivKey, - testStakingData.FinalityProviderKey, - testStakingData.StakingAmount, - &stakingTxHash, - 1, - stakingInfo, - stakingTx, - getCovenantPrivKeys(t), - regtestParams, - ) - tm.SendTxWithNConfirmations(t, unbondingTx, 1) - tm.CheckNextUnconfirmedEvent(t, uint64(stakingInfo.StakingOutput.Value), 0) - - // confirm the unbonding tx - tm.BitcoindHandler.GenerateBlocks(int(k)) - tm.WaitForNConfirmations(t, int(k)) - tm.CheckNextUnconfirmedEvent(t, 0, 0) - tm.CheckNextUnbondingEvent(t, unbondingTx.TxHash()) -} - -// TestIndexerRestart tests following cases upon restart -// 1. it restarts from a previous height before a staking tx is found. -// We expect the staking event to be replayed -// 2. it restarts exactly from the height it just processed. -// We expect the staking event not to be replayed -func TestIndexerRestart(t *testing.T) { - // ensure we have UTXOs - n := 101 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - // generate valid staking tx data - r := rand.New(rand.NewSource(time.Now().UnixNano())) - sysParams := tm.VersionedParams.Versions[0] - k := sysParams.ConfirmationDepth - testStakingData := datagen.GenerateTestStakingData(t, r, sysParams) - stakingInfo, err := btcstaking.BuildV0IdentifiableStakingOutputs( - sysParams.Tag, - tm.WalletPrivKey.PubKey(), - testStakingData.FinalityProviderKey, - sysParams.CovenantPks, - sysParams.CovenantQuorum, - testStakingData.StakingTime, - testStakingData.StakingAmount, - regtestParams, - ) - require.NoError(t, err) - - // send the staking tx and mine blocks - require.NoError(t, err) - stakingTx, err := testutils.CreateTxFromOutputsAndSign( - tm.WalletClient, - []*wire.TxOut{stakingInfo.OpReturnOutput, stakingInfo.StakingOutput}, - 1000, - tm.MinerAddr, - ) - require.NoError(t, err) - stakingTxHash := stakingTx.TxHash() - tm.SendTxWithNConfirmations(t, stakingTx, int(k)) - - // check that the staking tx is already stored - _ = tm.WaitForStakingTxStored(t, stakingTxHash) - - // check the staking event is received by the queue - tm.CheckNextStakingEvent(t, stakingTxHash) - - // restart from a height before staking tx - restartedTm := ReStartFromHeight(t, tm, uint64(n)) - defer restartedTm.Stop() - - // check the staking event is replayed - restartedTm.CheckNextStakingEvent(t, stakingTxHash) - - // restart the testing manager again from last processed height + 1 - restartedTm2 := ReStartFromHeight(t, restartedTm, restartedTm.Si.GetStartHeight()) - defer restartedTm2.Stop() - - // no staking event should be replayed as - // the indexer starts from a higher height - restartedTm2.CheckNoStakingEvent(t) -} - -// TestStakingUnbondingLifeCycle covers the following life cycle -// 1. the staking tx is sent to BTC -// 2. the staking tx is parsed by the indexer -// 3. the subsequent unbonding tx is sent to BTC -// 4. the unbonding tx is identified by the indexer -// 5. the subsequent withdraw tx is sent to BTC -// 6. the withdraw tx is identified by the indexer -func TestStakingUnbondingLifeCycle(t *testing.T) { - // ensure we have UTXOs - n := 101 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - // generate valid staking tx data - // TODO: test with multiple system parameters - sysParams := tm.VersionedParams.Versions[0] - k := uint64(sysParams.ConfirmationDepth) - testStakingData := getTestStakingData(t) - stakingInfo, err := btcstaking.BuildV0IdentifiableStakingOutputs( - sysParams.Tag, - tm.WalletPrivKey.PubKey(), - testStakingData.FinalityProviderKey, - sysParams.CovenantPks, - sysParams.CovenantQuorum, - testStakingData.StakingTime, - testStakingData.StakingAmount, - regtestParams, - ) - require.NoError(t, err) - - // send the staking tx and mine blocks - require.NoError(t, err) - stakingTx, err := testutils.CreateTxFromOutputsAndSign( - tm.WalletClient, - []*wire.TxOut{stakingInfo.OpReturnOutput, stakingInfo.StakingOutput}, - 1000, - tm.MinerAddr, - ) - require.NoError(t, err) - stakingTxHash := stakingTx.TxHash() - tm.SendTxWithNConfirmations(t, stakingTx, int(k)) - - // check that the staking tx is already stored - _ = tm.WaitForStakingTxStored(t, stakingTxHash) - - // check the staking event is received by the queue - tm.CheckNextStakingEvent(t, stakingTxHash) - - // build and send unbonding tx from the previous staking tx - storedStakingTx, err := tm.Si.GetStakingTxByHash(&stakingTxHash) - require.NoError(t, err) - require.NotNil(t, storedStakingTx) - unbondingTx, _ := testutils.BuildUnbondingTx( - t, - sysParams, - tm.WalletPrivKey, - testStakingData.FinalityProviderKey, - testStakingData.StakingAmount, - &stakingTxHash, - storedStakingTx.StakingOutputIdx, - stakingInfo, - stakingTx, - getCovenantPrivKeys(t), - regtestParams, - ) - tm.SendTxWithNConfirmations(t, unbondingTx, int(k)) - - // check the unbonding tx is already stored - tm.WaitForUnbondingTxStored(t, unbondingTx.TxHash()) - - // check the unbonding event is received - tm.CheckNextUnbondingEvent(t, unbondingTx.TxHash()) - - // wait for the unbonding tx expires - if uint64(sysParams.UnbondingTime) > k { - tm.BitcoindHandler.GenerateBlocks(int(uint64(sysParams.UnbondingTime) - k)) - } - - // build and send withdraw tx from the unbonding tx - unbondingInfo, err := btcstaking.BuildUnbondingInfo( - tm.WalletPrivKey.PubKey(), - []*btcec.PublicKey{testStakingData.FinalityProviderKey}, - sysParams.CovenantPks, - sysParams.CovenantQuorum, - sysParams.UnbondingTime, - testStakingData.StakingAmount.MulF64(0.9), - regtestParams, - ) - require.NoError(t, err) - withdrawSpendInfo, err := unbondingInfo.TimeLockPathSpendInfo() - require.NoError(t, err) - fundingInfo := &testutils.FundingInfo{ - // unbonding tx only has one output - FundTxOutput: unbondingTx.TxOut[0], - FundTxHash: unbondingTx.TxHash(), - FundTxOutputIndex: 0, - FundTxSpendInfo: withdrawSpendInfo, - LockTime: sysParams.UnbondingTime, - Value: testStakingData.StakingAmount, - } - withdrawTx := testutils.BuildWithdrawTx( - t, - tm.WalletPrivKey, - []*testutils.FundingInfo{fundingInfo}, - regtestParams, - ) - tm.SendTxWithNConfirmations(t, withdrawTx, int(k)) - - // wait until the indexer identifies the withdraw tx - tm.WaitForNConfirmations(t, int(k)) - - // check the withdraw event is consumed - tm.CheckNextWithdrawEvent(t, stakingTx.TxHash()) -} - -// TestTimeBasedCap tests the case where the time-based cap is applied -func TestTimeBasedCap(t *testing.T) { - // start from the height at which the time-based cap is effective - n := 110 - tm := StartManagerWithNBlocks(t, n, 100) - defer tm.Stop() - - r := rand.New(rand.NewSource(time.Now().UnixNano())) - sysParams := tm.VersionedParams.Versions[1] - k := uint64(sysParams.ConfirmationDepth) - - // build and send staking tx which should not overflow - stakingTx, _, _ := tm.BuildStakingTx(t, r, sysParams) - tm.SendTxWithNConfirmations(t, stakingTx, int(k)) - storedTx := tm.WaitForStakingTxStored(t, stakingTx.TxHash()) - require.False(t, storedTx.IsOverflow) - - // generate blocks so that the height is out of the cap height - tm.BitcoindHandler.GenerateBlocks(20) - currentHeight, err := tm.BitcoindHandler.GetBlockCount() - require.NoError(t, err) - require.Greater(t, uint64(currentHeight), sysParams.CapHeight) - - // send another staking tx which should be overflow - stakingTx2, _, _ := tm.BuildStakingTx(t, r, sysParams) - tm.SendTxWithNConfirmations(t, stakingTx2, int(k)) - storedTx2 := tm.WaitForStakingTxStored(t, stakingTx2.TxHash()) - require.True(t, storedTx2.IsOverflow) -} - -func TestBtcHeaders(t *testing.T) { - r := rand.New(rand.NewSource(10)) - blocksPerRetarget := 2016 - - initBlocksQnt := r.Intn(15) + blocksPerRetarget - btcd, btcClient := StartBtcClientAndBtcHandler(t, initBlocksQnt) - - // from zero height - infos, err := cli.BtcHeaderInfoList(btcClient, 0, uint64(initBlocksQnt), true) - require.NoError(t, err) - require.Equal(t, len(infos), initBlocksQnt+1) - - generatedBlocksQnt := r.Intn(15) + 2 - btcd.GenerateBlocks(generatedBlocksQnt) - totalBlks := initBlocksQnt + generatedBlocksQnt - - // check from height with interval - fromBlockHeight := blocksPerRetarget - 1 - toBlockHeight := totalBlks - 2 - - infos, err = cli.BtcHeaderInfoList(btcClient, uint64(fromBlockHeight), uint64(toBlockHeight), true) - require.NoError(t, err) - require.Equal(t, len(infos), int(toBlockHeight-fromBlockHeight)+1) - require.EqualValues(t, infos[len(infos)-1].Height, uint64(toBlockHeight)) - - // from retarget block - infos, err = cli.BtcHeaderInfoList(btcClient, uint64(blocksPerRetarget), uint64(totalBlks), true) - require.NoError(t, err) - require.Equal(t, len(infos), int(totalBlks-blocksPerRetarget)+1) -} - -func getCovenantPrivKeys(t *testing.T) []*btcec.PrivateKey { - // private keys of the covenant committee which correspond to the public keys in test-params.json - covenantPrivKeysHex := []string{ - "6a2369c2c9f5cd3c4242834228acdc38b73e5b8930f5f4a9b69e6eaf557e60ed", - } - - privKeys := make([]*btcec.PrivateKey, len(covenantPrivKeysHex)) - for i, skHex := range covenantPrivKeysHex { - skBytes, err := hex.DecodeString(skHex) - require.NoError(t, err) - sk, _ := btcec.PrivKeyFromBytes(skBytes) - privKeys[i] = sk - } - - return privKeys -} - -func getTestStakingData( - t *testing.T, -) *datagen.TestStakingData { - stakerPrivKey, err := btcec.NewPrivateKey() - require.NoError(t, err) - - fpPrivKey, err := btcec.NewPrivateKey() - require.NoError(t, err) - - stakingAmount := btcutil.Amount(100000) - stakingTime := uint16(100) - - return &datagen.TestStakingData{ - StakerKey: stakerPrivKey.PubKey(), - FinalityProviderKey: fpPrivKey.PubKey(), - StakingAmount: stakingAmount, - StakingTime: stakingTime, - } -} diff --git a/itest/queue_setup.go b/itest/queue_setup.go deleted file mode 100644 index 657144e..0000000 --- a/itest/queue_setup.go +++ /dev/null @@ -1,61 +0,0 @@ -package e2etest - -import ( - "fmt" - "strings" - "testing" - - "github.com/babylonlabs-io/staking-queue-client/client" - "github.com/babylonlabs-io/staking-queue-client/queuemngr" - "github.com/rabbitmq/amqp091-go" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/babylonlabs-io/staking-indexer/config" -) - -func setupTestQueueConsumer(t *testing.T, cfg *config.QueueConfig) (*queuemngr.QueueManager, error) { - amqpURI := fmt.Sprintf("amqp://%s:%s@%s", cfg.User, cfg.Password, cfg.Url) - conn, err := amqp091.Dial(amqpURI) - if err != nil { - t.Fatalf("failed to connect to RabbitMQ in test: %v", err) - } - defer conn.Close() - err = purgeQueues(conn, []string{ - client.ActiveStakingQueueName, - }) - if err != nil { - return nil, err - } - - // Start the actual queue processing in our codebase - - validQueueCfg, err := cfg.ToQueueClientConfig() - require.NoError(t, err) - queues, err := queuemngr.NewQueueManager(validQueueCfg, zap.NewNop()) - require.NoError(t, err) - - return queues, nil -} - -// purgeQueues purges all messages from the given list of queues. -func purgeQueues(conn *amqp091.Connection, queues []string) error { - ch, err := conn.Channel() - if err != nil { - return fmt.Errorf("failed to open a channel in test: %w", err) - } - defer ch.Close() - - for _, queue := range queues { - _, err := ch.QueuePurge(queue, false) - if err != nil { - if strings.Contains(err.Error(), "no queue") { - fmt.Printf("Queue '%s' not found, ignoring...\n", queue) - continue // Ignore this error and proceed with the next queue - } - return fmt.Errorf("failed to purge queue in test %s: %w", queue, err) - } - } - - return nil -} diff --git a/itest/scripts/start_rabbitmq.sh b/itest/scripts/start_rabbitmq.sh deleted file mode 100755 index 539d7b6..0000000 --- a/itest/scripts/start_rabbitmq.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# Check if the RabbitMQ container is already running -RABBITMQ_CONTAINER_NAME="rabbitmq" -if [ $(docker ps -q -f name=^/${RABBITMQ_CONTAINER_NAME}$) ]; then - echo "RabbitMQ container already running. Skipping RabbitMQ startup." -else - echo "Starting RabbitMQ" - # Start RabbitMQ - docker compose up -d rabbitmq -fi diff --git a/itest/test-params.json b/itest/test-params.json deleted file mode 100644 index 3480ddc..0000000 --- a/itest/test-params.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "versions": [ - { - "version": 0, - "activation_height": 100, - "staking_cap": 5000000000, - "tag": "01020304", - "covenant_pks": [ - "03cecdb3f9b99e0d67e806a9d1abd9d8c7811602dc7653bcb657a3faff29b76047" - ], - "covenant_quorum": 1, - "unbonding_time": 20, - "unbonding_fee": 1000, - "max_staking_amount": 300000, - "min_staking_amount": 3000, - "max_staking_time": 100, - "min_staking_time": 50, - "confirmation_depth": 10 - }, - { - "version": 1, - "activation_height": 110, - "cap_height": 120, - "tag": "01020304", - "covenant_pks": [ - "03cecdb3f9b99e0d67e806a9d1abd9d8c7811602dc7653bcb657a3faff29b76047" - ], - "covenant_quorum": 1, - "unbonding_time": 20, - "unbonding_fee": 1000, - "max_staking_amount": 300000, - "min_staking_amount": 3000, - "max_staking_time": 100, - "min_staking_time": 50, - "confirmation_depth": 10 - } - ] -} diff --git a/itest/test_manager.go b/itest/test_manager.go deleted file mode 100644 index 524f408..0000000 --- a/itest/test_manager.go +++ /dev/null @@ -1,436 +0,0 @@ -package e2etest - -import ( - "encoding/hex" - "encoding/json" - "math/rand" - "os" - "path/filepath" - "sync" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/btcstaking" - "github.com/babylonlabs-io/networks/parameters/parser" - queuecli "github.com/babylonlabs-io/staking-queue-client/client" - "github.com/babylonlabs-io/staking-queue-client/queuemngr" - "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/wire" - "github.com/lightningnetwork/lnd/kvdb" - "github.com/lightningnetwork/lnd/signal" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/babylonlabs-io/staking-indexer/btcclient" - "github.com/babylonlabs-io/staking-indexer/btcscanner" - "github.com/babylonlabs-io/staking-indexer/config" - "github.com/babylonlabs-io/staking-indexer/indexer" - "github.com/babylonlabs-io/staking-indexer/indexerstore" - "github.com/babylonlabs-io/staking-indexer/log" - "github.com/babylonlabs-io/staking-indexer/params" - "github.com/babylonlabs-io/staking-indexer/server" - "github.com/babylonlabs-io/staking-indexer/testutils" - "github.com/babylonlabs-io/staking-indexer/testutils/datagen" -) - -type TestManager struct { - Config *config.Config - Db kvdb.Backend - Si *indexer.StakingIndexer - BS *btcscanner.BtcPoller - WalletPrivKey *btcec.PrivateKey - serverStopper *signal.Interceptor - wg *sync.WaitGroup - BitcoindHandler *BitcoindTestHandler - WalletClient *rpcclient.Client - MinerAddr btcutil.Address - DirPath string - QueueConsumer *queuemngr.QueueManager - StakingEventChan <-chan queuecli.QueueMessage - UnbondingEventChan <-chan queuecli.QueueMessage - WithdrawEventChan <-chan queuecli.QueueMessage - BtcInfoEventChan <-chan queuecli.QueueMessage - ConfirmedInfoEventChan <-chan queuecli.QueueMessage - VersionedParams *parser.ParsedGlobalParams -} - -// bitcoin params used for testing -var ( - regtestParams = &chaincfg.RegressionNetParams - eventuallyWaitTimeOut = 1 * time.Minute - eventuallyPollTime = 500 * time.Millisecond - testParamsPath = "test-params.json" - Passphrase = "pass" - WalletName = "test-wallet" -) - -func StartManagerWithNBlocks(t *testing.T, n int, startHeight uint64) *TestManager { - h := NewBitcoindHandler(t) - h.Start() - _ = h.CreateWallet(WalletName, Passphrase) - resp := h.GenerateBlocks(n) - - minerAddressDecoded, err := btcutil.DecodeAddress(resp.Address, regtestParams) - require.NoError(t, err) - - dirPath := filepath.Join(t.TempDir(), "sid", "e2etest") - err = os.MkdirAll(dirPath, 0750) - require.NoError(t, err) - - return StartWithBitcoinHandler(t, h, minerAddressDecoded, dirPath, startHeight) -} - -func StartBtcClientAndBtcHandler(t *testing.T, generateNBlocks int) (*BitcoindTestHandler, *btcclient.BTCClient) { - btcd := NewBitcoindHandler(t) - btcd.Start() - _ = btcd.CreateWallet(WalletName, Passphrase) - - resp := btcd.GenerateBlocks(generateNBlocks) - require.Equal(t, len(resp.Blocks), generateNBlocks) - - cfg := DefaultStakingIndexerConfig(t.TempDir()) - btcClient, err := btcclient.NewBTCClient( - cfg.BTCConfig, - zap.NewNop(), - ) - require.NoError(t, err) - - return btcd, btcClient -} - -func StartWithBitcoinHandler(t *testing.T, h *BitcoindTestHandler, minerAddress btcutil.Address, dirPath string, startHeight uint64) *TestManager { - cfg := DefaultStakingIndexerConfig(dirPath) - logger, err := log.NewRootLoggerWithFile(config.LogFile(dirPath), "debug") - require.NoError(t, err) - - rpcclient, err := rpcclient.New(cfg.BTCConfig.ToConnConfig(), nil) - require.NoError(t, err) - err = rpcclient.WalletPassphrase(Passphrase, 200) - require.NoError(t, err) - walletPrivKey, err := rpcclient.DumpPrivKey(minerAddress) - require.NoError(t, err) - - btcClient, err := btcclient.NewBTCClient( - cfg.BTCConfig, - logger, - ) - require.NoError(t, err) - - btcNotifier, err := btcscanner.NewBTCNotifier( - cfg.BTCConfig, - &cfg.BTCNetParams, - &btcscanner.EmptyHintCache{}, - ) - require.NoError(t, err) - - paramsRetriever, err := params.NewGlobalParamsRetriever(testParamsPath) - require.NoError(t, err) - versionedParams := paramsRetriever.VersionedParams() - require.NoError(t, err) - scanner, err := btcscanner.NewBTCScanner(versionedParams.Versions[0].ConfirmationDepth, logger, btcClient, btcNotifier) - require.NoError(t, err) - - // create event consumer - queueConsumer, err := setupTestQueueConsumer(t, cfg.QueueConfig) - require.NoError(t, err) - - stakingEventChan, err := queueConsumer.StakingQueue.ReceiveMessages() - require.NoError(t, err) - unbondingEventChan, err := queueConsumer.UnbondingQueue.ReceiveMessages() - require.NoError(t, err) - withdrawEventChan, err := queueConsumer.WithdrawQueue.ReceiveMessages() - require.NoError(t, err) - unconfirmedEventChan, err := queueConsumer.BtcInfoQueue.ReceiveMessages() - require.NoError(t, err) - confirmedInfoEventChan, err := queueConsumer.ConfirmedInfoQueue.ReceiveMessages() - require.NoError(t, err) - - db, err := cfg.DatabaseConfig.GetDbBackend() - require.NoError(t, err) - si, err := indexer.NewStakingIndexer(cfg, logger, queueConsumer, db, versionedParams, scanner) - require.NoError(t, err) - - interceptor, err := signal.Intercept() - require.NoError(t, err) - - service := server.NewStakingIndexerServer( - cfg, - queueConsumer, - db, - btcNotifier, - si, - logger, - interceptor, - ) - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - err := service.RunUntilShutdown(startHeight) - require.NoError(t, err) - }() - // Wait for the server to start - time.Sleep(3 * time.Second) - - return &TestManager{ - Config: cfg, - Si: si, - BS: scanner, - serverStopper: &interceptor, - wg: &wg, - BitcoindHandler: h, - WalletClient: rpcclient, - WalletPrivKey: walletPrivKey.PrivKey, - MinerAddr: minerAddress, - DirPath: dirPath, - QueueConsumer: queueConsumer, - StakingEventChan: stakingEventChan, - UnbondingEventChan: unbondingEventChan, - WithdrawEventChan: withdrawEventChan, - BtcInfoEventChan: unconfirmedEventChan, - ConfirmedInfoEventChan: confirmedInfoEventChan, - VersionedParams: versionedParams, - } -} - -func (tm *TestManager) Stop() { - tm.serverStopper.RequestShutdown() - tm.wg.Wait() -} - -func ReStartFromHeight(t *testing.T, tm *TestManager, height uint64) *TestManager { - t.Logf("restarting the test manager from height %d", height) - tm.Stop() - - restartedTm := StartWithBitcoinHandler(t, tm.BitcoindHandler, tm.MinerAddr, tm.DirPath, height) - - t.Log("the test manager is restarted") - - return restartedTm -} - -func DefaultStakingIndexerConfig(homePath string) *config.Config { - defaultConfig := config.DefaultConfigWithHome(homePath) - - // enable emitting extra events for testing - defaultConfig.ExtraEventEnabled = true - - // both wallet and node are bitcoind - defaultConfig.BTCNetParams = *regtestParams - - bitcoindHost := "127.0.0.1:18443" - bitcoindUser := "user" - bitcoindPass := "pass" - - defaultConfig.BTCConfig.RPCHost = bitcoindHost - defaultConfig.BTCConfig.RPCUser = bitcoindUser - defaultConfig.BTCConfig.RPCPass = bitcoindPass - defaultConfig.BTCConfig.BlockPollingInterval = 1 * time.Second - defaultConfig.BTCConfig.TxPollingInterval = 1 * time.Second - - return defaultConfig -} - -func (tm *TestManager) BuildStakingTx( - t *testing.T, - r *rand.Rand, - params *parser.ParsedVersionedGlobalParams, -) (*wire.MsgTx, *datagen.TestStakingData, *btcstaking.IdentifiableStakingInfo) { - testStakingData := datagen.GenerateTestStakingData(t, r, params) - stakingInfo, err := btcstaking.BuildV0IdentifiableStakingOutputs( - params.Tag, - tm.WalletPrivKey.PubKey(), - testStakingData.FinalityProviderKey, - params.CovenantPks, - params.CovenantQuorum, - testStakingData.StakingTime, - testStakingData.StakingAmount, - regtestParams, - ) - require.NoError(t, err) - - stakingTx, err := testutils.CreateTxFromOutputsAndSign( - tm.WalletClient, - []*wire.TxOut{stakingInfo.OpReturnOutput, stakingInfo.StakingOutput}, - 1000, - tm.MinerAddr, - ) - require.NoError(t, err) - - return stakingTx, testStakingData, stakingInfo -} - -func (tm *TestManager) SendTxWithNConfirmations(t *testing.T, tx *wire.MsgTx, n int) { - txHash, err := tm.WalletClient.SendRawTransaction(tx, true) - require.NoError(t, err) - - require.Eventually(t, func() bool { - txFromMempool := retrieveTransactionFromMempool(t, tm.WalletClient, []*chainhash.Hash{txHash}) - return len(txFromMempool) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - mBlock := tm.mineNBlock(t, n) - require.Equal(t, 2, len(mBlock.Transactions)) - t.Logf("sent tx %s with %d confirmations", txHash.String(), n) -} - -func retrieveTransactionFromMempool(t *testing.T, client *rpcclient.Client, hashes []*chainhash.Hash) []*btcutil.Tx { - var txes []*btcutil.Tx - for _, txHash := range hashes { - tx, err := client.GetRawTransaction(txHash) - require.NoError(t, err) - txes = append(txes, tx) - } - return txes -} - -func (tm *TestManager) mineNBlock(t *testing.T, n int) *wire.MsgBlock { - resp := tm.BitcoindHandler.GenerateBlocks(n) - hash, err := chainhash.NewHashFromStr(resp.Blocks[0]) - require.NoError(t, err) - header, err := tm.WalletClient.GetBlock(hash) - require.NoError(t, err) - return header -} - -func (tm *TestManager) WaitForNConfirmations(t *testing.T, n int) { - currentHeight, err := tm.BitcoindHandler.GetBlockCount() - require.NoError(t, err) - require.Eventually(t, func() bool { - confirmedTip := tm.BS.LastConfirmedHeight() - return confirmedTip == uint64(currentHeight)-uint64(n)+1 - }, eventuallyWaitTimeOut, eventuallyPollTime) -} - -func (tm *TestManager) CheckNextStakingEvent(t *testing.T, stakingTxHash chainhash.Hash) { - stakingEventBytes := <-tm.StakingEventChan - var activeStakingEvent queuecli.ActiveStakingEvent - err := json.Unmarshal([]byte(stakingEventBytes.Body), &activeStakingEvent) - require.NoError(t, err) - - storedStakingTx, err := tm.Si.GetStakingTxByHash(&stakingTxHash) - require.NotNil(t, storedStakingTx) - require.NoError(t, err) - require.Equal(t, stakingTxHash.String(), activeStakingEvent.StakingTxHashHex) - require.Equal(t, storedStakingTx.Tx.TxHash().String(), activeStakingEvent.StakingTxHashHex) - require.Equal(t, uint64(storedStakingTx.StakingTime), activeStakingEvent.StakingTimeLock) - require.Equal(t, storedStakingTx.StakingValue, activeStakingEvent.StakingValue) - require.Equal(t, uint64(storedStakingTx.StakingOutputIdx), activeStakingEvent.StakingOutputIndex) - require.Equal(t, storedStakingTx.InclusionHeight, activeStakingEvent.StakingStartHeight) - require.Equal(t, storedStakingTx.IsOverflow, activeStakingEvent.IsOverflow) - require.Equal(t, hex.EncodeToString(schnorr.SerializePubKey(storedStakingTx.StakerPk)), activeStakingEvent.StakerPkHex) - require.Equal(t, hex.EncodeToString(schnorr.SerializePubKey(storedStakingTx.FinalityProviderPk)), activeStakingEvent.FinalityProviderPkHex) - - err = tm.QueueConsumer.StakingQueue.DeleteMessage(stakingEventBytes.Receipt) - require.NoError(t, err) -} - -func (tm *TestManager) CheckNoStakingEvent(t *testing.T) { - select { - case _, ok := <-tm.StakingEventChan: - require.False(t, ok) - default: - return - } -} - -func (tm *TestManager) CheckNextUnbondingEvent(t *testing.T, unbondingTxHash chainhash.Hash) { - unbondingEventBytes := <-tm.UnbondingEventChan - var unbondingEvent queuecli.UnbondingStakingEvent - err := json.Unmarshal([]byte(unbondingEventBytes.Body), &unbondingEvent) - require.NoError(t, err) - require.Equal(t, unbondingTxHash.String(), unbondingEvent.UnbondingTxHashHex) - - storedUnbondingTx, err := tm.Si.GetUnbondingTxByHash(&unbondingTxHash) - require.NoError(t, err) - require.NotNil(t, storedUnbondingTx) - require.Equal(t, storedUnbondingTx.Tx.TxHash().String(), unbondingEvent.UnbondingTxHashHex) - require.Equal(t, storedUnbondingTx.StakingTxHash.String(), unbondingEvent.StakingTxHashHex) - - err = tm.QueueConsumer.UnbondingQueue.DeleteMessage(unbondingEventBytes.Receipt) - require.NoError(t, err) -} - -func (tm *TestManager) CheckNextWithdrawEvent(t *testing.T, stakingTxHash chainhash.Hash) { - withdrawEventBytes := <-tm.WithdrawEventChan - var withdrawEvent queuecli.WithdrawStakingEvent - err := json.Unmarshal([]byte(withdrawEventBytes.Body), &withdrawEvent) - require.NoError(t, err) - require.Equal(t, stakingTxHash.String(), withdrawEvent.StakingTxHashHex) - - err = tm.QueueConsumer.WithdrawQueue.DeleteMessage(withdrawEventBytes.Receipt) - require.NoError(t, err) -} - -func (tm *TestManager) CheckConfirmedInfoEvent(t *testing.T, height, tvl uint64) { - var confirmedInfoEv queuecli.ConfirmedInfoEvent - - for { - confirmedInfoEventBytes := <-tm.ConfirmedInfoEventChan - err := tm.QueueConsumer.ConfirmedInfoQueue.DeleteMessage(confirmedInfoEventBytes.Receipt) - require.NoError(t, err) - err = json.Unmarshal([]byte(confirmedInfoEventBytes.Body), &confirmedInfoEv) - require.NoError(t, err) - if height != confirmedInfoEv.Height { - continue - } - require.Equal(t, confirmedInfoEv.Tvl, tvl) - return - } -} - -func (tm *TestManager) CheckNextUnconfirmedEvent(t *testing.T, confirmedTvl, totalTvl uint64) { - var btcInfoEvent queuecli.BtcInfoEvent - - for { - btcInfoEventBytes := <-tm.BtcInfoEventChan - err := tm.QueueConsumer.BtcInfoQueue.DeleteMessage(btcInfoEventBytes.Receipt) - require.NoError(t, err) - err = json.Unmarshal([]byte(btcInfoEventBytes.Body), &btcInfoEvent) - require.NoError(t, err) - if confirmedTvl != btcInfoEvent.ConfirmedTvl { - continue - } - if totalTvl != btcInfoEvent.UnconfirmedTvl { - continue - } - - return - } -} - -func (tm *TestManager) WaitForStakingTxStored(t *testing.T, txHash chainhash.Hash) *indexerstore.StoredStakingTransaction { - var storedTx indexerstore.StoredStakingTransaction - require.Eventually(t, func() bool { - storedStakingTx, err := tm.Si.GetStakingTxByHash(&txHash) - if err != nil || storedStakingTx == nil { - return false - } - storedTx = *storedStakingTx - return true - }, eventuallyWaitTimeOut, eventuallyPollTime) - - require.Equal(t, txHash.String(), storedTx.Tx.TxHash().String()) - - return &storedTx -} - -func (tm *TestManager) WaitForUnbondingTxStored(t *testing.T, txHash chainhash.Hash) { - var storedTx indexerstore.StoredUnbondingTransaction - require.Eventually(t, func() bool { - storedUnbondingTx, err := tm.Si.GetUnbondingTxByHash(&txHash) - if err != nil || storedUnbondingTx == nil { - return false - } - storedTx = *storedUnbondingTx - return true - }, eventuallyWaitTimeOut, eventuallyPollTime) - - require.Equal(t, txHash.String(), storedTx.Tx.TxHash().String()) -} From ee182f5f2762c3a28f9a835edc09efbcd74b563a Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 13:50:47 +0530 Subject: [PATCH 03/32] intro e2etest --- e2etest/atomicslasher_e2e_test.go | 290 +++++++++ e2etest/bitcoind_node_setup.go | 121 ++++ e2etest/container/config.go | 37 ++ e2etest/container/container.go | 253 ++++++++ e2etest/log.go | 10 + e2etest/monitor_e2e_test.go | 150 +++++ e2etest/reporter_e2e_test.go | 243 ++++++++ e2etest/slasher_e2e_test.go | 290 +++++++++ e2etest/submitter_e2e_test.go | 195 ++++++ e2etest/test_manager.go | 291 +++++++++ e2etest/test_manager_btcstaking.go | 898 +++++++++++++++++++++++++++ e2etest/unbondingwatcher_e2e_test.go | 336 ++++++++++ e2etest/wallet.db | Bin 0 -> 196608 bytes testutil/datagen/datagen.go | 18 + testutil/datagen/reporter.go | 271 ++++++++ testutil/mocks/btcclient.go | 448 +++++++++++++ testutil/port.go | 61 ++ testutil/store.go | 26 + testutil/version.go | 32 + 19 files changed, 3970 insertions(+) create mode 100644 e2etest/atomicslasher_e2e_test.go create mode 100644 e2etest/bitcoind_node_setup.go create mode 100644 e2etest/container/config.go create mode 100644 e2etest/container/container.go create mode 100644 e2etest/log.go create mode 100644 e2etest/monitor_e2e_test.go create mode 100644 e2etest/reporter_e2e_test.go create mode 100644 e2etest/slasher_e2e_test.go create mode 100644 e2etest/submitter_e2e_test.go create mode 100644 e2etest/test_manager.go create mode 100644 e2etest/test_manager_btcstaking.go create mode 100644 e2etest/unbondingwatcher_e2e_test.go create mode 100644 e2etest/wallet.db create mode 100644 testutil/datagen/datagen.go create mode 100644 testutil/datagen/reporter.go create mode 100644 testutil/mocks/btcclient.go create mode 100644 testutil/port.go create mode 100644 testutil/store.go create mode 100644 testutil/version.go diff --git a/e2etest/atomicslasher_e2e_test.go b/e2etest/atomicslasher_e2e_test.go new file mode 100644 index 0000000..8ada2f9 --- /dev/null +++ b/e2etest/atomicslasher_e2e_test.go @@ -0,0 +1,290 @@ +//go:build e2e +// +build e2e + +package e2etest + +import ( + "go.uber.org/zap" + "testing" + "time" + + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + "github.com/babylonlabs-io/vigilante/btcclient" + bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" + "github.com/babylonlabs-io/vigilante/btcstaking-tracker/btcslasher" + "github.com/babylonlabs-io/vigilante/config" + "github.com/babylonlabs-io/vigilante/metrics" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/stretchr/testify/require" +) + +// TestAtomicSlasher verifies the behavior of the atomic slasher by setting up delegations, +// sending slashing transactions, and ensuring that slashing is detected and executed correctly. +func TestAtomicSlasher(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's needed by staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + + // start WebSocket connection with Babylon for subscriber services + err := tm.BabylonClient.Start() + require.NoError(t, err) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &btcclient.EmptyHintCache{}, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + backend, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + metrics.NewBTCStakingTrackerMetrics(), + ) + go bsTracker.Start() + defer bsTracker.Stop() + + bsParamsResp, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + bsParams := bsParamsResp.Params + + // set up a finality provider + btcFP, fpSK := tm.CreateFinalityProvider(t) + // set up 2 BTC delegations + tm.CreateBTCDelegation(t, fpSK) + tm.CreateBTCDelegation(t, fpSK) + + // retrieve 2 BTC delegations + btcDelsResp, err := tm.BabylonClient.BTCDelegations(bstypes.BTCDelegationStatus_ACTIVE, nil) + require.NoError(t, err) + require.Len(t, btcDelsResp.BtcDelegations, 2) + btcDels := btcDelsResp.BtcDelegations + + /* + finality provider builds slashing tx witness and sends slashing tx to Bitcoin + */ + victimBTCDel := btcDels[0] + victimSlashingTx, err := btcslasher.BuildSlashingTxWithWitness(victimBTCDel, &bsParams, regtestParams, fpSK) + // send slashing tx to Bitcoin + require.NoError(t, err) + slashingTxHash, err := tm.BTCClient.SendRawTransaction(victimSlashingTx, true) + require.NoError(t, err) + + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(slashingTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // mine a block that includes slashing tx, which will trigger atomic slasher + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + minedBlock := tm.mineBlock(t) + require.Equal(t, 2, len(minedBlock.Transactions)) + + /* + atomic slasher will detect the selective slashing on victim BTC delegation + the finality provider will get slashed on Babylon + */ + require.Eventually(t, func() bool { + resp, err := tm.BabylonClient.FinalityProvider(btcFP.BtcPk.MarshalHex()) + if err != nil { + return false + } + return resp.FinalityProvider.SlashedBabylonHeight > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + /* + atomic slasher will slash the other BTC delegation on Bitcoin + */ + btcDel2 := btcDels[1] + slashTx2, err := bstypes.NewBTCSlashingTxFromHex(btcDel2.SlashingTxHex) + require.NoError(t, err) + slashingTxHash2 := slashTx2.MustGetTxHash() + + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(slashingTxHash2) + if err != nil { + t.Logf("err of getting slashingTxHash of the BTC delegation affected by atomic slashing: %v", err) + } + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // mine a block that includes slashing tx, which will trigger atomic slasher + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash2})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + minedBlock = tm.mineBlock(t) + require.Equal(t, 2, len(minedBlock.Transactions)) +} + +// TestAtomicSlasher_Unbonding tests the atomic slasher's handling of unbonding BTC delegations, +// including the creation and detection of unbonding slashing transactions. +func TestAtomicSlasher_Unbonding(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's needed by staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + + // start WebSocket connection with Babylon for subscriber services + err := tm.BabylonClient.Start() + require.NoError(t, err) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + emptyHintCache := btcclient.EmptyHintCache{} + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &emptyHintCache, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + backend, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + go bsTracker.Start() + defer bsTracker.Stop() + + bsParamsResp, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + bsParams := bsParamsResp.Params + + // set up a finality provider + btcFP, fpSK := tm.CreateFinalityProvider(t) + + // set up 1st BTC delegation, which will be later used as the victim + stakingSlashingInfo, unbondingSlashingInfo, btcDelSK := tm.CreateBTCDelegation(t, fpSK) + btcDelsResp, err := tm.BabylonClient.BTCDelegations(bstypes.BTCDelegationStatus_ACTIVE, nil) + require.NoError(t, err) + require.Len(t, btcDelsResp.BtcDelegations, 1) + victimBTCDel := btcDelsResp.BtcDelegations[0] + + // set up 2nd BTC delegation, which will be subjected to atomic slashing + tm.CreateBTCDelegation(t, fpSK) + btcDelsResp2, err := tm.BabylonClient.BTCDelegations(bstypes.BTCDelegationStatus_ACTIVE, nil) + require.NoError(t, err) + require.Len(t, btcDelsResp2.BtcDelegations, 2) + + // NOTE: `BTCDelegations` API does not return BTC delegations in created time order + // thus we need to find out the 2nd BTC delegation one-by-one + var btcDel2 *bstypes.BTCDelegationResponse + for _, delResp2 := range btcDelsResp2.BtcDelegations { + if delResp2.StakingTxHex != victimBTCDel.StakingTxHex { + btcDel2 = delResp2 + break + } + } + + require.NotNil(t, btcDel2, "err second delegation not found") + + /* + the victim BTC delegation unbonds + */ + tm.Undelegate(t, stakingSlashingInfo, unbondingSlashingInfo, btcDelSK, func() { tm.CatchUpBTCLightClient(t) }) + + /* + finality provider builds unbonding slashing tx witness and sends it to Bitcoin + */ + victimUnbondingSlashingTx, err := btcslasher.BuildUnbondingSlashingTxWithWitness(victimBTCDel, &bsParams, regtestParams, fpSK) + require.NoError(t, err) + + // send slashing tx to Bitcoin + // NOTE: sometimes unbonding slashing tx is not immediately spendable for some reason + var unbondingSlashingTxHash *chainhash.Hash + require.Eventually(t, func() bool { + unbondingSlashingTxHash, err = tm.BTCClient.SendRawTransaction(victimUnbondingSlashingTx, true) + if err != nil { + t.Logf("err of SendRawTransaction: %v", err) + return false + } + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // unbonding slashing tx is eventually queryable + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(unbondingSlashingTxHash) + if err != nil { + t.Logf("err of GetRawTransaction: %v", err) + return false + } + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + // mine a block that includes unbonding slashing tx, which will trigger atomic slasher + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingSlashingTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + minedBlock := tm.mineBlock(t) + require.Equal(t, 2, len(minedBlock.Transactions)) + + /* + atomic slasher will detect the selective slashing on victim BTC delegation + the finality provider will get slashed on Babylon + */ + require.Eventually(t, func() bool { + resp, err := tm.BabylonClient.FinalityProvider(btcFP.BtcPk.MarshalHex()) + if err != nil { + return false + } + return resp.FinalityProvider.SlashedBabylonHeight > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + /* + atomic slasher will slash the other BTC delegation on Bitcoin + */ + slashingTx2, err := bstypes.NewBTCSlashingTxFromHex(btcDel2.SlashingTxHex) + require.NoError(t, err) + + slashingTxHash2 := slashingTx2.MustGetTxHash() + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(slashingTxHash2) + t.Logf("err of getting slashingTxHash of the BTC delegation affected by atomic slashing: %v", err) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // mine a block that includes slashing tx, which will trigger atomic slasher + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash2})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + minedBlock = tm.mineBlock(t) + require.Equal(t, 2, len(minedBlock.Transactions)) +} diff --git a/e2etest/bitcoind_node_setup.go b/e2etest/bitcoind_node_setup.go new file mode 100644 index 0000000..7a7a096 --- /dev/null +++ b/e2etest/bitcoind_node_setup.go @@ -0,0 +1,121 @@ +package e2etest + +import ( + "encoding/json" + "fmt" + "github.com/babylonlabs-io/vigilante/e2etest/container" + "github.com/ory/dockertest/v3" + "github.com/stretchr/testify/require" + "os" + "strconv" + "strings" + "testing" + "time" +) + +var ( + startTimeout = 30 * time.Second +) + +type CreateWalletResponse struct { + Name string `json:"name"` + Warning string `json:"warning"` +} + +type GenerateBlockResponse struct { + // address of the recipient of rewards + Address string `json:"address"` + // blocks generated + Blocks []string `json:"blocks"` +} + +type BitcoindTestHandler struct { + t *testing.T + m *container.Manager +} + +func NewBitcoindHandler(t *testing.T, manager *container.Manager) *BitcoindTestHandler { + return &BitcoindTestHandler{ + t: t, + m: manager, + } +} + +func (h *BitcoindTestHandler) Start(t *testing.T) *dockertest.Resource { + tempPath, err := os.MkdirTemp("", "vigilante-test-*") + require.NoError(h.t, err) + + h.t.Cleanup(func() { + _ = os.RemoveAll(tempPath) + }) + + bitcoinResource, err := h.m.RunBitcoindResource(t, tempPath) + require.NoError(h.t, err) + + h.t.Cleanup(func() { + _ = h.m.ClearResources() + }) + + require.Eventually(h.t, func() bool { + _, err := h.GetBlockCount() + if err != nil { + h.t.Logf("failed to get block count: %v", err) + } + return err == nil + }, startTimeout, 500*time.Millisecond, "bitcoind did not start") + + return bitcoinResource +} + +// GetBlockCount retrieves the current number of blocks in the blockchain from the Bitcoind. +func (h *BitcoindTestHandler) GetBlockCount() (int, error) { + buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"getblockcount"}) + if err != nil { + return 0, err + } + + parsedBuffStr := strings.TrimSuffix(buff.String(), "\n") + + return strconv.Atoi(parsedBuffStr) +} + +// GenerateBlocks mines a specified number of blocks in the Bitcoind. +func (h *BitcoindTestHandler) GenerateBlocks(count int) *GenerateBlockResponse { + buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"-generate", fmt.Sprintf("%d", count)}) + require.NoError(h.t, err) + + var response GenerateBlockResponse + err = json.Unmarshal(buff.Bytes(), &response) + require.NoError(h.t, err) + + return &response +} + +// CreateWallet creates a new wallet with the specified name and passphrase in the Bitcoind +func (h *BitcoindTestHandler) CreateWallet(walletName string, passphrase string) *CreateWalletResponse { + // last arg is true which indicates we are using descriptor wallets they do not allow dumping private keys. + buff, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"createwallet", walletName, "false", "false", passphrase, "false", "true"}) + require.NoError(h.t, err) + + var response CreateWalletResponse + err = json.Unmarshal(buff.Bytes(), &response) + require.NoError(h.t, err) + + return &response +} + +// InvalidateBlock invalidates blocks starting from specified block hash +func (h *BitcoindTestHandler) InvalidateBlock(blockHash string) { + _, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"invalidateblock", blockHash}) + require.NoError(h.t, err) +} + +// ImportDescriptors imports a given Bitcoin address descriptor into the Bitcoind +func (h *BitcoindTestHandler) ImportDescriptors(descriptor string) { + _, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"importdescriptors", descriptor}) + require.NoError(h.t, err) +} + +func (h *BitcoindTestHandler) Stop() { + _ = h.m.ClearResources() +} diff --git a/e2etest/container/config.go b/e2etest/container/config.go new file mode 100644 index 0000000..c4f79f5 --- /dev/null +++ b/e2etest/container/config.go @@ -0,0 +1,37 @@ +package container + +import ( + "testing" + + "github.com/babylonlabs-io/babylon-staking-indexer/testutil" + "github.com/stretchr/testify/require" +) + +// ImageConfig contains all images and their respective tags +// needed for running e2e tests. +type ImageConfig struct { + BitcoindRepository string + BitcoindVersion string + BabylonRepository string + BabylonVersion string +} + +//nolint:deadcode +const ( + dockerBitcoindRepository = "lncm/bitcoind" + dockerBitcoindVersionTag = "v27.0" + dockerBabylondRepository = "babylonlabs/babylond" +) + +// NewImageConfig returns ImageConfig needed for running e2e test. +func NewImageConfig(t *testing.T) ImageConfig { + babylonVersion, err := testutil.GetBabylonVersion() + require.NoError(t, err) + + return ImageConfig{ + BitcoindRepository: dockerBitcoindRepository, + BitcoindVersion: dockerBitcoindVersionTag, + BabylonRepository: dockerBabylondRepository, + BabylonVersion: babylonVersion, + } +} diff --git a/e2etest/container/container.go b/e2etest/container/container.go new file mode 100644 index 0000000..ca369aa --- /dev/null +++ b/e2etest/container/container.go @@ -0,0 +1,253 @@ +package container + +import ( + "bytes" + "context" + "fmt" + "regexp" + "strconv" + "testing" + "time" + + "github.com/babylonlabs-io/babylon-staking-indexer/testutil" + bbn "github.com/babylonlabs-io/babylon/types" + "github.com/btcsuite/btcd/btcec/v2" + + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "github.com/stretchr/testify/require" +) + +const ( + bitcoindContainerName = "bitcoind" + babylondContainerName = "babylond" +) + +var ( + _, covenantPK = btcec.PrivKeyFromBytes( + []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + ) +) + +var errRegex = regexp.MustCompile(`(E|e)rror`) + +// Manager is a wrapper around all Docker instances, and the Docker API. +// It provides utilities to run and interact with all Docker containers used within e2e testing. +type Manager struct { + cfg ImageConfig + pool *dockertest.Pool + resources map[string]*dockertest.Resource +} + +// NewManager creates a new Manager instance and initializes +// all Docker specific utilities. Returns an error if initialization fails. +func NewManager(t *testing.T) (docker *Manager, err error) { + docker = &Manager{ + cfg: NewImageConfig(t), + resources: make(map[string]*dockertest.Resource), + } + docker.pool, err = dockertest.NewPool("") + if err != nil { + return nil, err + } + + return docker, nil +} + +func (m *Manager) ExecBitcoindCliCmd(t *testing.T, command []string) (bytes.Buffer, bytes.Buffer, error) { + // this is currently hardcoded, as it will be the same for all tests + cmd := []string{"bitcoin-cli", "-chain=regtest", "-rpcuser=user", "-rpcpassword=pass"} + cmd = append(cmd, command...) + return m.ExecCmd(t, bitcoindContainerName, cmd) +} + +// ExecCmd executes command by running it on the given container. +// It word for word `error` in output to discern between error and regular output. +// It retures stdout and stderr as bytes.Buffer and an error if the command fails. +func (m *Manager) ExecCmd(t *testing.T, containerName string, command []string) (bytes.Buffer, bytes.Buffer, error) { + if _, ok := m.resources[containerName]; !ok { + return bytes.Buffer{}, bytes.Buffer{}, fmt.Errorf("no resource %s found", containerName) + } + containerId := m.resources[containerName].Container.ID + + var ( + outBuf bytes.Buffer + errBuf bytes.Buffer + ) + + timeout := 120 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + t.Logf("\n\nRunning: \"%s\"", command) + + // We use the `require.Eventually` function because it is only allowed to do one transaction per block without + // sequence numbers. For simplicity, we avoid keeping track of the sequence number and just use the `require.Eventually`. + require.Eventually( + t, + func() bool { + exec, err := m.pool.Client.CreateExec(docker.CreateExecOptions{ + Context: ctx, + AttachStdout: true, + AttachStderr: true, + Container: containerId, + User: "root", + Cmd: command, + }) + + if err != nil { + t.Logf("failed to create exec: %v", err) + return false + } + + err = m.pool.Client.StartExec(exec.ID, docker.StartExecOptions{ + Context: ctx, + Detach: false, + OutputStream: &outBuf, + ErrorStream: &errBuf, + }) + if err != nil { + t.Logf("failed to start exec: %v", err) + return false + } + + errBufString := errBuf.String() + // Note that this does not match all errors. + // This only works if CLI outputs "Error" or "error" + // to stderr. + if errRegex.MatchString(errBufString) { + t.Log("\nstderr:") + t.Log(errBufString) + + t.Log("\nstdout:") + t.Log(outBuf.String()) + return false + } + + return true + }, + timeout, + 500*time.Millisecond, + "command failed", + ) + + return outBuf, errBuf, nil +} + +// RunBitcoindResource starts a bitcoind docker container +func (m *Manager) RunBitcoindResource( + t *testing.T, + bitcoindCfgPath string, +) (*dockertest.Resource, error) { + bitcoindResource, err := m.pool.RunWithOptions( + &dockertest.RunOptions{ + Name: fmt.Sprintf("%s-%s", bitcoindContainerName, t.Name()), + Repository: m.cfg.BitcoindRepository, + Tag: m.cfg.BitcoindVersion, + User: "root:root", + Labels: map[string]string{ + "e2e": "bitcoind", + }, + Mounts: []string{ + fmt.Sprintf("%s/:/data/.bitcoin", bitcoindCfgPath), + }, + ExposedPorts: []string{ + "18443/tcp", + }, + Cmd: []string{ + "-regtest", + "-txindex", + "-rpcuser=user", + "-rpcpassword=pass", + "-rpcallowip=0.0.0.0/0", + "-rpcbind=0.0.0.0", + "-zmqpubsequence=tcp://0.0.0.0:28333", + "-fallbackfee=0.0002", + }, + }, + func(config *docker.HostConfig) { + config.PortBindings = map[docker.Port][]docker.PortBinding{ + "18443/tcp": {{HostIP: "", HostPort: strconv.Itoa(testutil.AllocateUniquePort(t))}}, // only expose what we need + } + config.PublishAllPorts = false // because in dockerfile they already expose them + }, + noRestart, + ) + if err != nil { + return nil, err + } + m.resources[bitcoindContainerName] = bitcoindResource + return bitcoindResource, nil +} + +// RunBabylondResource starts a babylond container +func (m *Manager) RunBabylondResource( + t *testing.T, + mounthPath string, + baseHeaderHex string, + slashingPkScript string, + epochInterval uint, +) (*dockertest.Resource, error) { + cmd := []string{ + "sh", "-c", fmt.Sprintf( + "babylond testnet --v=1 --output-dir=/home --starting-ip-address=192.168.10.2 "+ + "--keyring-backend=test --chain-id=chain-test --btc-finalization-timeout=4 "+ + "--btc-confirmation-depth=2 --additional-sender-account --btc-network=regtest "+ + "--min-staking-time-blocks=200 --min-staking-amount-sat=10000 "+ + "--epoch-interval=%d --slashing-pk-script=%s --btc-base-header=%s "+ + "--covenant-quorum=1 --covenant-pks=%s && chmod -R 777 /home && babylond start --home=/home/node0/babylond", + epochInterval, slashingPkScript, baseHeaderHex, bbn.NewBIP340PubKeyFromBTCPK(covenantPK).MarshalHex()), + } + + resource, err := m.pool.RunWithOptions( + &dockertest.RunOptions{ + Name: fmt.Sprintf("%s-%s", babylondContainerName, t.Name()), + Repository: m.cfg.BabylonRepository, + Tag: m.cfg.BabylonVersion, + Labels: map[string]string{ + "e2e": "babylond", + }, + User: "root:root", + Mounts: []string{ + fmt.Sprintf("%s/:/home/", mounthPath), + }, + ExposedPorts: []string{ + "9090/tcp", // only expose what we need + "26657/tcp", + }, + Cmd: cmd, + }, + func(config *docker.HostConfig) { + config.PortBindings = map[docker.Port][]docker.PortBinding{ + "9090/tcp": {{HostIP: "", HostPort: strconv.Itoa(testutil.AllocateUniquePort(t))}}, + "26657/tcp": {{HostIP: "", HostPort: strconv.Itoa(testutil.AllocateUniquePort(t))}}, + } + }, + noRestart, + ) + if err != nil { + return nil, err + } + + m.resources[babylondContainerName] = resource + + return resource, nil +} + +// ClearResources removes all outstanding Docker resources created by the Manager. +func (m *Manager) ClearResources() error { + for _, resource := range m.resources { + if err := m.pool.Purge(resource); err != nil { + return err + } + } + + return nil +} + +func noRestart(config *docker.HostConfig) { + // in this case, we don't want the nodes to restart on failure + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } +} diff --git a/e2etest/log.go b/e2etest/log.go new file mode 100644 index 0000000..b7a99f5 --- /dev/null +++ b/e2etest/log.go @@ -0,0 +1,10 @@ +package e2etest + +import ( + "github.com/babylonlabs-io/vigilante/config" +) + +var ( + logger, _ = config.NewRootLogger("auto", "debug") + log = logger.Sugar() +) diff --git a/e2etest/monitor_e2e_test.go b/e2etest/monitor_e2e_test.go new file mode 100644 index 0000000..844a89c --- /dev/null +++ b/e2etest/monitor_e2e_test.go @@ -0,0 +1,150 @@ +//go:build e2e +// +build e2e + +package e2etest + +import ( + "fmt" + bbnclient "github.com/babylonlabs-io/babylon/client/client" + "github.com/babylonlabs-io/vigilante/btcclient" + "github.com/babylonlabs-io/vigilante/metrics" + "github.com/babylonlabs-io/vigilante/monitor" + "github.com/babylonlabs-io/vigilante/reporter" + "github.com/babylonlabs-io/vigilante/submitter" + "github.com/babylonlabs-io/vigilante/testutil" + "github.com/babylonlabs-io/vigilante/types" + "github.com/btcsuite/btcd/chaincfg" + sdk "github.com/cosmos/cosmos-sdk/types" + promtestutil "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "time" + + "testing" +) + +// TestMonitorBootstrap - validates that after a restart monitor bootstraps from DB +func TestMonitorBootstrap(t *testing.T) { + t.Parallel() + numMatureOutputs := uint32(150) + + tm := StartManager(t, numMatureOutputs, 2) + defer tm.Stop(t) + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &btcclient.EmptyHintCache{}, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + dbBackend := testutil.MakeTestBackend(t) + + monitorMetrics := metrics.NewMonitorMetrics() + genesisPath := fmt.Sprintf("%s/config/genesis.json", tm.Config.Babylon.KeyDirectory) + genesisInfo, err := types.GetGenesisInfoFromFile(genesisPath) + require.NoError(t, err) + + tm.Config.Submitter.PollingIntervalSeconds = 1 + subAddr, _ := sdk.AccAddressFromBech32(submitterAddrStr) + + // create submitter + vigilantSubmitter, _ := submitter.New( + &tm.Config.Submitter, + logger, + tm.BTCClient, + tm.BabylonClient, + subAddr, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + tm.Config.Common.MaxRetryTimes, + metrics.NewSubmitterMetrics(), + testutil.MakeTestBackend(t), + tm.Config.BTC.WalletName, + ) + + vigilantSubmitter.Start() + defer vigilantSubmitter.Stop() + + vigilantReporter, err := reporter.New( + &tm.Config.Reporter, + logger, + tm.BTCClient, + tm.BabylonClient, + backend, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + metrics.NewReporterMetrics(), + ) + require.NoError(t, err) + + defer func() { + vigilantSubmitter.Stop() + vigilantSubmitter.WaitForShutdown() + }() + + mon, err := monitor.New( + &tm.Config.Monitor, + &tm.Config.Common, + zap.NewNop(), + genesisInfo, + tm.BabylonClient, + tm.BTCClient, + backend, + monitorMetrics, + dbBackend, + ) + require.NoError(t, err) + vigilantReporter.Start() + defer vigilantReporter.Stop() + + go func() { + ticker := time.NewTicker(3 * time.Second) + timer := time.NewTimer(15 * time.Second) + defer timer.Stop() + defer ticker.Stop() + for { + select { + case <-ticker.C: + tm.mineBlock(t) + case <-timer.C: + return + } + } + }() + + go mon.Start(genesisInfo.GetBaseBTCHeight()) + + time.Sleep(15 * time.Second) + mon.Stop() + + // use a new bbn client + babylonClient, err := bbnclient.New(&tm.Config.Babylon, nil) + require.NoError(t, err) + defer babylonClient.Stop() + + mon, err = monitor.New( + &tm.Config.Monitor, + &tm.Config.Common, + zap.NewNop(), + genesisInfo, + babylonClient, + tm.BTCClient, + backend, + monitorMetrics, + dbBackend, + ) + require.NoError(t, err) + go mon.Start(genesisInfo.GetBaseBTCHeight()) + + defer mon.Stop() + + require.Zero(t, promtestutil.ToFloat64(mon.Metrics().InvalidBTCHeadersCounter)) + require.Zero(t, promtestutil.ToFloat64(mon.Metrics().InvalidEpochsCounter)) + require.Eventually(t, func() bool { + return mon.BTCScanner.GetBaseHeight() > genesisInfo.GetBaseBTCHeight() + }, eventuallyWaitTimeOut, eventuallyPollTime) +} diff --git a/e2etest/reporter_e2e_test.go b/e2etest/reporter_e2e_test.go new file mode 100644 index 0000000..accb120 --- /dev/null +++ b/e2etest/reporter_e2e_test.go @@ -0,0 +1,243 @@ +//go:build e2e +// +build e2e + +package e2etest + +import ( + "sync" + "testing" + "time" + + "github.com/babylonlabs-io/vigilante/btcclient" + "github.com/babylonlabs-io/vigilante/netparams" + + "github.com/babylonlabs-io/vigilante/metrics" + "github.com/babylonlabs-io/vigilante/reporter" + "github.com/stretchr/testify/require" +) + +var ( + longEventuallyWaitTimeOut = 2 * eventuallyWaitTimeOut +) + +func (tm *TestManager) BabylonBTCChainMatchesBtc(t *testing.T) bool { + tipHeight, err := tm.TestRpcClient.GetBlockCount() + require.NoError(t, err) + tipHash, err := tm.TestRpcClient.GetBlockHash(tipHeight) + require.NoError(t, err) + bbnBtcLcTip, err := tm.BabylonClient.BTCHeaderChainTip() + require.NoError(t, err) + + return uint32(tipHeight) == bbnBtcLcTip.Header.Height && tipHash.String() == bbnBtcLcTip.Header.HashHex +} + +func (tm *TestManager) GenerateAndSubmitBlockNBlockStartingFromDepth(t *testing.T, N int, depth uint32) { + if depth == 0 { + // depth 0 means we are starting from tip + tm.BitcoindHandler.GenerateBlocks(N) + return + } + + height, err := tm.TestRpcClient.GetBlockCount() + require.NoError(t, err) + + startingBlockHeight := height - int64(depth) + + blockHash, err := tm.TestRpcClient.GetBlockHash(startingBlockHeight) + require.NoError(t, err) + + // invalidate blocks from this height + tm.BitcoindHandler.InvalidateBlock(blockHash.String()) + + for i := 0; i < N; i++ { + tm.BitcoindHandler.GenerateBlocks(N) + } +} + +func TestReporter_BoostrapUnderFrequentBTCHeaders(t *testing.T) { + //t.Parallel() // todo(lazar): this test when run in parallel is very flaky, investigate why + // no need to much mature outputs, we are not going to submit transactions in this test + numMatureOutputs := uint32(150) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + + reporterMetrics := metrics.NewReporterMetrics() + + // create the chain notifier + btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) + require.NoError(t, err) + btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) + btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) + require.NoError(t, err) + + vigilantReporter, err := reporter.New( + &tm.Config.Reporter, + logger, + tm.BTCClient, + tm.BabylonClient, + btcNotifier, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + reporterMetrics, + ) + require.NoError(t, err) + + // start a routine that mines BTC blocks very fast + var wg sync.WaitGroup + stopChan := make(chan struct{}) + + wg.Add(1) + go func() { + defer wg.Done() + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + tm.BitcoindHandler.GenerateBlocks(1) + case <-stopChan: + return + } + } + }() + + // mine some BTC headers + tm.BitcoindHandler.GenerateBlocks(1) + + // start reporter + vigilantReporter.Start() + defer vigilantReporter.Stop() + + // tips should eventually match + require.Eventually(t, func() bool { + return tm.BabylonBTCChainMatchesBtc(t) + }, longEventuallyWaitTimeOut, eventuallyPollTime) + + close(stopChan) + wg.Wait() +} + +func TestRelayHeadersAndHandleRollbacks(t *testing.T) { + t.Parallel() + // no need to much mature outputs, we are not going to submit transactions in this test + numMatureOutputs := uint32(150) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + // this is necessary to receive notifications about new transactions entering mempool + defer tm.Stop(t) + + reporterMetrics := metrics.NewReporterMetrics() + + btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) + require.NoError(t, err) + btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) + btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) + require.NoError(t, err) + + vigilantReporter, err := reporter.New( + &tm.Config.Reporter, + logger, + tm.BTCClient, + tm.BabylonClient, + btcNotifier, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + reporterMetrics, + ) + require.NoError(t, err) + + vigilantReporter.Start() + defer vigilantReporter.Stop() + + require.Eventually(t, func() bool { + return tm.BabylonBTCChainMatchesBtc(t) + }, longEventuallyWaitTimeOut, eventuallyPollTime) + + // generate 3, we are submitting headers 1 by 1 so we use small amount as this is slow process + tm.BitcoindHandler.GenerateBlocks(3) + + require.Eventually(t, func() bool { + return tm.BabylonBTCChainMatchesBtc(t) + }, longEventuallyWaitTimeOut, eventuallyPollTime) + + // we will start from block before tip and submit 2 new block this should trigger rollback + tm.GenerateAndSubmitBlockNBlockStartingFromDepth(t, 2, 1) + + // tips should eventually match + require.Eventually(t, func() bool { + return tm.BabylonBTCChainMatchesBtc(t) + }, longEventuallyWaitTimeOut, eventuallyPollTime) +} + +func TestHandleReorgAfterRestart(t *testing.T) { + t.Parallel() + // no need to much mature outputs, we are not going to submit transactions in this test + numMatureOutputs := uint32(150) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + // this is necessary to receive notifications about new transactions entering mempool + defer tm.Stop(t) + + reporterMetrics := metrics.NewReporterMetrics() + + btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) + require.NoError(t, err) + btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) + btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) + require.NoError(t, err) + + vigilantReporter, err := reporter.New( + &tm.Config.Reporter, + logger, + tm.BTCClient, + tm.BabylonClient, + btcNotifier, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + reporterMetrics, + ) + require.NoError(t, err) + + vigilantReporter.Start() + + require.Eventually(t, func() bool { + return tm.BabylonBTCChainMatchesBtc(t) + }, longEventuallyWaitTimeOut, eventuallyPollTime) + + // At this point babylon is inline with btc. Now: + // Kill reporter + // and generate reorg on btc + // start reporter again + // Even though reorg happened, reporter should be able to provide better chain + // in bootstrap phase + + vigilantReporter.Stop() + vigilantReporter.WaitForShutdown() + + // // we will start from block before tip and submit 2 new block this should trigger rollback + tm.GenerateAndSubmitBlockNBlockStartingFromDepth(t, 2, 1) + + btcClient := initBTCClientWithSubscriber(t, tm.Config) //current tm.btcClient already has an active zmq subscription, would panic + defer btcClient.Stop() + + // Start new reporter + vigilantReporterNew, err := reporter.New( + &tm.Config.Reporter, + logger, + btcClient, + tm.BabylonClient, + btcNotifier, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + reporterMetrics, + ) + require.NoError(t, err) + + vigilantReporterNew.Start() + + // Headers should match even though reorg happened + require.Eventually(t, func() bool { + return tm.BabylonBTCChainMatchesBtc(t) + }, longEventuallyWaitTimeOut, eventuallyPollTime) +} diff --git a/e2etest/slasher_e2e_test.go b/e2etest/slasher_e2e_test.go new file mode 100644 index 0000000..c8e36bb --- /dev/null +++ b/e2etest/slasher_e2e_test.go @@ -0,0 +1,290 @@ +//go:build e2e +// +build e2e + +package e2etest + +import ( + "go.uber.org/zap" + "testing" + "time" + + "github.com/btcsuite/btcd/chaincfg" + + "github.com/babylonlabs-io/vigilante/btcclient" + bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" + "github.com/babylonlabs-io/vigilante/config" + "github.com/babylonlabs-io/vigilante/metrics" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/stretchr/testify/require" +) + +func TestSlasher_GracefulShutdown(t *testing.T) { + t.Parallel() + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + emptyHintCache := btcclient.EmptyHintCache{} + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &emptyHintCache, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + backend, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + + go bsTracker.Start() + + // wait for bootstrapping + time.Sleep(10 * time.Second) + + tm.BTCClient.Stop() + // gracefully shut down + defer bsTracker.Stop() +} + +func TestSlasher_Slasher(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's needed by staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, 5) + defer tm.Stop(t) + // start WebSocket connection with Babylon for subscriber services + err := tm.BabylonClient.Start() + require.NoError(t, err) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + emptyHintCache := btcclient.EmptyHintCache{} + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &emptyHintCache, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + backend, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + go bsTracker.Start() + defer bsTracker.Stop() + + // wait for bootstrapping + time.Sleep(5 * time.Second) + + // set up a finality provider + _, fpSK := tm.CreateFinalityProvider(t) + // set up a BTC delegation + stakingSlashingInfo, _, _ := tm.CreateBTCDelegation(t, fpSK) + + // commit public randomness, vote and equivocate + tm.VoteAndEquivocate(t, fpSK) + + // slashing tx will eventually enter mempool + slashingMsgTx, err := stakingSlashingInfo.SlashingTx.ToMsgTx() + require.NoError(t, err) + slashingMsgTxHash1 := slashingMsgTx.TxHash() + slashingMsgTxHash := &slashingMsgTxHash1 + + // mine a block that includes slashing tx + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + minedBlock := tm.mineBlock(t) + // ensure 2 txs will eventually be received (staking tx and slashing tx) + require.Equal(t, 2, len(minedBlock.Transactions)) +} + +func TestSlasher_SlashingUnbonding(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's needed by staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, 5) + defer tm.Stop(t) + // start WebSocket connection with Babylon for subscriber services + err := tm.BabylonClient.Start() + require.NoError(t, err) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + emptyHintCache := btcclient.EmptyHintCache{} + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &emptyHintCache, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + backend, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + go bsTracker.Start() + defer bsTracker.Stop() + + // wait for bootstrapping + time.Sleep(5 * time.Second) + + // set up a finality provider + _, fpSK := tm.CreateFinalityProvider(t) + // set up a BTC delegation + _, _, _ = tm.CreateBTCDelegation(t, fpSK) + // set up a BTC delegation + stakingSlashingInfo1, unbondingSlashingInfo1, stakerPrivKey1 := tm.CreateBTCDelegation(t, fpSK) + + // undelegate + unbondingSlashingInfo, _ := tm.Undelegate(t, stakingSlashingInfo1, unbondingSlashingInfo1, stakerPrivKey1, func() { tm.CatchUpBTCLightClient(t) }) + + // commit public randomness, vote and equivocate + tm.VoteAndEquivocate(t, fpSK) + + // slashing tx will eventually enter mempool + unbondingSlashingMsgTx, err := unbondingSlashingInfo.SlashingTx.ToMsgTx() + require.NoError(t, err) + unbondingSlashingMsgTxHash1 := unbondingSlashingMsgTx.TxHash() + unbondingSlashingMsgTxHash := &unbondingSlashingMsgTxHash1 + + // slash unbonding tx will eventually enter mempool + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(unbondingSlashingMsgTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // mine a block that includes slashing tx + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingSlashingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + tm.mineBlock(t) + + // ensure tx is eventually on Bitcoin + require.Eventually(t, func() bool { + res, err := tm.BTCClient.GetRawTransactionVerbose(unbondingSlashingMsgTxHash) + if err != nil { + return false + } + return len(res.BlockHash) > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) +} + +func TestSlasher_Bootstrapping(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's needed by staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, 5) + defer tm.Stop(t) + // start WebSocket connection with Babylon for subscriber services + err := tm.BabylonClient.Start() + require.NoError(t, err) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + // set up a finality provider + _, fpSK := tm.CreateFinalityProvider(t) + // set up a BTC delegation + stakingSlashingInfo, _, _ := tm.CreateBTCDelegation(t, fpSK) + + // commit public randomness, vote and equivocate + tm.VoteAndEquivocate(t, fpSK) + + emptyHintCache := btcclient.EmptyHintCache{} + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &emptyHintCache, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + backend, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + + // bootstrap BTC staking tracker + err = bsTracker.Bootstrap(0) + require.NoError(t, err) + + // slashing tx will eventually enter mempool + slashingMsgTx, err := stakingSlashingInfo.SlashingTx.ToMsgTx() + require.NoError(t, err) + slashingMsgTxHash1 := slashingMsgTx.TxHash() + slashingMsgTxHash := &slashingMsgTxHash1 + + // mine a block that includes slashing tx + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + minedBlock := tm.mineBlock(t) + // ensure 2 txs will eventually be received (staking tx and slashing tx) + require.Equal(t, 2, len(minedBlock.Transactions)) +} diff --git a/e2etest/submitter_e2e_test.go b/e2etest/submitter_e2e_test.go new file mode 100644 index 0000000..81dc024 --- /dev/null +++ b/e2etest/submitter_e2e_test.go @@ -0,0 +1,195 @@ +//go:build e2e +// +build e2e + +package e2etest + +import ( + "github.com/babylonlabs-io/vigilante/testutil" + promtestutil "github.com/prometheus/client_golang/prometheus/testutil" + "math/rand" + "testing" + "time" + + "github.com/babylonlabs-io/babylon/testutil/datagen" + btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" + checkpointingtypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/babylonlabs-io/vigilante/metrics" + "github.com/babylonlabs-io/vigilante/submitter" +) + +func TestSubmitterSubmission(t *testing.T) { + t.Parallel() + r := rand.New(rand.NewSource(time.Now().Unix())) + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + + randomCheckpoint := datagen.GenRandomRawCheckpointWithMeta(r) + randomCheckpoint.Status = checkpointingtypes.Sealed + randomCheckpoint.Ckpt.EpochNum = 1 + + ctl := gomock.NewController(t) + mockBabylonClient := submitter.NewMockBabylonQueryClient(ctl) + subAddr, _ := sdk.AccAddressFromBech32(submitterAddrStr) + + mockBabylonClient.EXPECT().BTCCheckpointParams().Return( + &btcctypes.QueryParamsResponse{ + Params: btcctypes.Params{ + CheckpointTag: babylonTagHex, + BtcConfirmationDepth: 2, + CheckpointFinalizationTimeout: 4, + }, + }, nil) + mockBabylonClient.EXPECT().RawCheckpointList(gomock.Any(), gomock.Any()).Return( + &checkpointingtypes.QueryRawCheckpointListResponse{ + RawCheckpoints: []*checkpointingtypes.RawCheckpointWithMetaResponse{ + randomCheckpoint.ToResponse(), + }, + }, nil).AnyTimes() + + tm.Config.Submitter.PollingIntervalSeconds = 2 + + // create submitter + vigilantSubmitter, _ := submitter.New( + &tm.Config.Submitter, + logger, + tm.BTCClient, + mockBabylonClient, + subAddr, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + tm.Config.Common.MaxRetryTimes, + metrics.NewSubmitterMetrics(), + testutil.MakeTestBackend(t), + tm.Config.BTC.WalletName, + ) + + vigilantSubmitter.Start() + + defer func() { + vigilantSubmitter.Stop() + vigilantSubmitter.WaitForShutdown() + }() + + // wait for our 2 op_returns with epoch 1 checkpoint to hit the mempool + var mempoolTxs []*chainhash.Hash + require.Eventually(t, func() bool { + var err error + mempoolTxs, err = tm.BTCClient.GetRawMempool() + require.NoError(t, err) + return len(mempoolTxs) > 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + require.NotNil(t, mempoolTxs) + + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, mempoolTxs)) == 2 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // mine a block with those transactions + blockWithOpReturnTransactions := tm.mineBlock(t) + // block should have 3 transactions, 2 from submitter and 1 coinbase + require.Equal(t, len(blockWithOpReturnTransactions.Transactions), 3) +} + +func TestSubmitterSubmissionReplace(t *testing.T) { + t.Parallel() + r := rand.New(rand.NewSource(time.Now().Unix())) + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + + randomCheckpoint := datagen.GenRandomRawCheckpointWithMeta(r) + randomCheckpoint.Status = checkpointingtypes.Sealed + randomCheckpoint.Ckpt.EpochNum = 1 + + ctl := gomock.NewController(t) + mockBabylonClient := submitter.NewMockBabylonQueryClient(ctl) + subAddr, _ := sdk.AccAddressFromBech32(submitterAddrStr) + + mockBabylonClient.EXPECT().BTCCheckpointParams().Return( + &btcctypes.QueryParamsResponse{ + Params: btcctypes.Params{ + CheckpointTag: babylonTagHex, + BtcConfirmationDepth: 2, + CheckpointFinalizationTimeout: 4, + }, + }, nil) + mockBabylonClient.EXPECT().RawCheckpointList(gomock.Any(), gomock.Any()).Return( + &checkpointingtypes.QueryRawCheckpointListResponse{ + RawCheckpoints: []*checkpointingtypes.RawCheckpointWithMetaResponse{ + randomCheckpoint.ToResponse(), + }, + }, nil).AnyTimes() + + tm.Config.Submitter.PollingIntervalSeconds = 2 + tm.Config.Submitter.ResendIntervalSeconds = 2 + tm.Config.Submitter.ResubmitFeeMultiplier = 2.1 + // create submitter + vigilantSubmitter, _ := submitter.New( + &tm.Config.Submitter, + logger, + tm.BTCClient, + mockBabylonClient, + subAddr, + tm.Config.Common.RetrySleepTime, + tm.Config.Common.MaxRetrySleepTime, + tm.Config.Common.MaxRetryTimes, + metrics.NewSubmitterMetrics(), + testutil.MakeTestBackend(t), + tm.Config.BTC.WalletName, + ) + + vigilantSubmitter.Start() + + defer func() { + vigilantSubmitter.Stop() + vigilantSubmitter.WaitForShutdown() + }() + + // wait for our 2 op_returns with epoch 1 checkpoint to hit the mempool and then + // retrieve them from there + txsMap := make(map[string]struct{}) + var sendTransactions []*btcutil.Tx + + var mempoolTxs []*chainhash.Hash + require.Eventually(t, func() bool { + var err error + mempoolTxs, err = tm.BTCClient.GetRawMempool() + require.NoError(t, err) + for _, hash := range mempoolTxs { + hashStr := hash.String() + if _, exists := txsMap[hashStr]; !exists { + tx, err := tm.BTCClient.GetRawTransaction(hash) + require.NoError(t, err) + txsMap[hashStr] = struct{}{} + sendTransactions = append(sendTransactions, tx) + } + } + return len(txsMap) == 3 + }, eventuallyWaitTimeOut, 50*time.Millisecond) + + resendTx2 := sendTransactions[2] + + // Here check that sendTransactions1 are replacements for sendTransactions, i.e they should have: + // 1. same + // 2. outputs with different values + // 3. different signatures + require.Equal(t, sendTransactions[1].MsgTx().TxIn[0].PreviousOutPoint, resendTx2.MsgTx().TxIn[0].PreviousOutPoint) + require.Less(t, resendTx2.MsgTx().TxOut[1].Value, sendTransactions[1].MsgTx().TxOut[1].Value) + require.NotEqual(t, sendTransactions[1].MsgTx().TxIn[0].Witness[0], resendTx2.MsgTx().TxIn[0].Witness[0]) + + // mine a block with those replacement transactions just to be sure they execute correctly + blockWithOpReturnTransactions := tm.mineBlock(t) + // block should have 2 transactions, 1 from submitter and 1 coinbase + require.Equal(t, len(blockWithOpReturnTransactions.Transactions), 3) + require.True(t, promtestutil.ToFloat64(vigilantSubmitter.Metrics().ResentCheckpointsCounter) == 1) +} diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go new file mode 100644 index 0000000..16df8a2 --- /dev/null +++ b/e2etest/test_manager.go @@ -0,0 +1,291 @@ +package e2etest + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/babylonlabs-io/vigilante/e2etest/container" + "github.com/btcsuite/btcd/txscript" + pv "github.com/cosmos/relayer/v2/relayer/provider" + "go.uber.org/zap" + "os" + "path/filepath" + "testing" + "time" + + bbnclient "github.com/babylonlabs-io/babylon/client/client" + bbn "github.com/babylonlabs-io/babylon/types" + btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + "github.com/babylonlabs-io/vigilante/btcclient" + "github.com/babylonlabs-io/vigilante/config" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/rpcclient" + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" +) + +var ( + submitterAddrStr = "bbn1eppc73j56382wjn6nnq3quu5eye4pmm087xfdh" //nolint:unused + babylonTag = []byte{1, 2, 3, 4} //nolint:unused + babylonTagHex = hex.EncodeToString(babylonTag) //nolint:unused + + eventuallyWaitTimeOut = 40 * time.Second + eventuallyPollTime = 1 * time.Second + regtestParams = &chaincfg.RegressionNetParams + defaultEpochInterval = uint(400) //nolint:unused +) + +func defaultVigilanteConfig() *config.Config { + defaultConfig := config.DefaultConfig() + defaultConfig.BTC.NetParams = regtestParams.Name + defaultConfig.BTC.Endpoint = "127.0.0.1:18443" + // Config setting necessary to connect btcwallet daemon + defaultConfig.BTC.WalletPassword = "pass" + defaultConfig.BTC.Username = "user" + defaultConfig.BTC.Password = "pass" + defaultConfig.BTC.ZmqSeqEndpoint = config.DefaultZmqSeqEndpoint + + return defaultConfig +} + +type TestManager struct { + TestRpcClient *rpcclient.Client + BitcoindHandler *BitcoindTestHandler + BabylonClient *bbnclient.Client + BTCClient *btcclient.Client + Config *config.Config + WalletPrivKey *btcec.PrivateKey + manger *container.Manager +} + +func initBTCClientWithSubscriber(t *testing.T, cfg *config.Config) *btcclient.Client { + client, err := btcclient.NewWallet(cfg, zap.NewNop()) + require.NoError(t, err) + + // let's wait until chain rpc becomes available + // poll time is increase here to avoid spamming the rpc server + require.Eventually(t, func() bool { + if _, err := client.GetBlockCount(); err != nil { + log.Errorf("failed to get best block: %v", err) + return false + } + + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + + return client +} + +// StartManager creates a test manager +// NOTE: uses btc client with zmq +func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval uint) *TestManager { + manager, err := container.NewManager(t) + require.NoError(t, err) + + btcHandler := NewBitcoindHandler(t, manager) + bitcoind := btcHandler.Start(t) + passphrase := "pass" + _ = btcHandler.CreateWallet("default", passphrase) + + cfg := defaultVigilanteConfig() + + cfg.BTC.Endpoint = fmt.Sprintf("127.0.0.1:%s", bitcoind.GetPort("18443/tcp")) + + testRpcClient, err := rpcclient.New(&rpcclient.ConnConfig{ + Host: cfg.BTC.Endpoint, + User: cfg.BTC.Username, + Pass: cfg.BTC.Password, + DisableTLS: true, + DisableConnectOnNew: true, + DisableAutoReconnect: false, + HTTPPostMode: true, + }, nil) + require.NoError(t, err) + + err = testRpcClient.WalletPassphrase(passphrase, 600) + require.NoError(t, err) + + walletPrivKey, err := importPrivateKey(btcHandler) + require.NoError(t, err) + blocksResponse := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) + + btcClient := initBTCClientWithSubscriber(t, cfg) + + var buff bytes.Buffer + err = regtestParams.GenesisBlock.Header.Serialize(&buff) + require.NoError(t, err) + baseHeaderHex := hex.EncodeToString(buff.Bytes()) + + minerAddressDecoded, err := btcutil.DecodeAddress(blocksResponse.Address, regtestParams) + require.NoError(t, err) + + pkScript, err := txscript.PayToAddrScript(minerAddressDecoded) + require.NoError(t, err) + + // start Babylon node + + tmpDir, err := tempDir(t) + require.NoError(t, err) + + babylond, err := manager.RunBabylondResource(t, tmpDir, baseHeaderHex, hex.EncodeToString(pkScript), epochInterval) + require.NoError(t, err) + + // create Babylon client + cfg.Babylon.KeyDirectory = filepath.Join(tmpDir, "node0", "babylond") + cfg.Babylon.Key = "test-spending-key" // keyring to bbn node + cfg.Babylon.GasAdjustment = 3.0 + + // update port with the dynamically allocated one from docker + cfg.Babylon.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) + cfg.Babylon.GRPCAddr = fmt.Sprintf("https://localhost:%s", babylond.GetPort("9090/tcp")) + + babylonClient, err := bbnclient.New(&cfg.Babylon, nil) + require.NoError(t, err) + + // wait until Babylon is ready + require.Eventually(t, func() bool { + resp, err := babylonClient.CurrentEpoch() + if err != nil { + return false + } + log.Infof("Babylon is ready: %v", resp) + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + + return &TestManager{ + TestRpcClient: testRpcClient, + BabylonClient: babylonClient, + BitcoindHandler: btcHandler, + BTCClient: btcClient, + Config: cfg, + WalletPrivKey: walletPrivKey, + manger: manager, + } +} + +func (tm *TestManager) Stop(t *testing.T) { + if tm.BabylonClient.IsRunning() { + err := tm.BabylonClient.Stop() + require.NoError(t, err) + } +} + +// mineBlock mines a single block +func (tm *TestManager) mineBlock(t *testing.T) *wire.MsgBlock { + resp := tm.BitcoindHandler.GenerateBlocks(1) + + hash, err := chainhash.NewHashFromStr(resp.Blocks[0]) + require.NoError(t, err) + + header, err := tm.TestRpcClient.GetBlock(hash) + require.NoError(t, err) + + return header +} + +func (tm *TestManager) MustGetBabylonSigner() string { + return tm.BabylonClient.MustGetAddr() +} + +// RetrieveTransactionFromMempool fetches transactions from the mempool for the given hashes +func (tm *TestManager) RetrieveTransactionFromMempool(t *testing.T, hashes []*chainhash.Hash) []*btcutil.Tx { + var txs []*btcutil.Tx + for _, txHash := range hashes { + tx, err := tm.BTCClient.GetRawTransaction(txHash) + require.NoError(t, err) + txs = append(txs, tx) + } + + return txs +} + +func (tm *TestManager) InsertBTCHeadersToBabylon(headers []*wire.BlockHeader) (*pv.RelayerTxResponse, error) { + var headersBytes []bbn.BTCHeaderBytes + + for _, h := range headers { + headersBytes = append(headersBytes, bbn.NewBTCHeaderBytesFromBlockHeader(h)) + } + + msg := btclctypes.MsgInsertHeaders{ + Headers: headersBytes, + Signer: tm.MustGetBabylonSigner(), + } + + return tm.BabylonClient.InsertHeaders(context.Background(), &msg) +} + +func (tm *TestManager) CatchUpBTCLightClient(t *testing.T) { + btcHeight, err := tm.TestRpcClient.GetBlockCount() + require.NoError(t, err) + + tipResp, err := tm.BabylonClient.BTCHeaderChainTip() + require.NoError(t, err) + btclcHeight := tipResp.Header.Height + + var headers []*wire.BlockHeader + for i := int(btclcHeight + 1); i <= int(btcHeight); i++ { + hash, err := tm.TestRpcClient.GetBlockHash(int64(i)) + require.NoError(t, err) + header, err := tm.TestRpcClient.GetBlockHeader(hash) + require.NoError(t, err) + headers = append(headers, header) + } + + _, err = tm.InsertBTCHeadersToBabylon(headers) + require.NoError(t, err) +} + +func importPrivateKey(btcHandler *BitcoindTestHandler) (*btcec.PrivateKey, error) { + privKey, err := btcec.NewPrivateKey() + if err != nil { + return nil, err + } + + wif, err := btcutil.NewWIF(privKey, regtestParams, true) + if err != nil { + return nil, err + } + + // "combo" allows us to import a key and handle multiple types of btc scripts with a single descriptor command. + descriptor := fmt.Sprintf("combo(%s)", wif.String()) + + // Create the JSON descriptor object. + descJSON, err := json.Marshal([]map[string]interface{}{ + { + "desc": descriptor, + "active": true, + "timestamp": "now", // tells Bitcoind to start scanning from the current blockchain height + "label": "test key", + }, + }) + + if err != nil { + return nil, err + } + + btcHandler.ImportDescriptors(string(descJSON)) + + return privKey, nil +} + +func tempDir(t *testing.T) (string, error) { + tempPath, err := os.MkdirTemp(os.TempDir(), "babylon-test-*") + if err != nil { + return "", err + } + + if err = os.Chmod(tempPath, 0777); err != nil { + return "", err + } + + t.Cleanup(func() { + _ = os.RemoveAll(tempPath) + }) + + return tempPath, err +} diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go new file mode 100644 index 0000000..2527e81 --- /dev/null +++ b/e2etest/test_manager_btcstaking.go @@ -0,0 +1,898 @@ +package e2etest + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "math/rand" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/avast/retry-go/v4" + "github.com/babylonlabs-io/babylon-staking-indexer/types" + "github.com/babylonlabs-io/babylon/btcstaking" + txformat "github.com/babylonlabs-io/babylon/btctxformatter" + "github.com/babylonlabs-io/babylon/crypto/eots" + asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" + "github.com/babylonlabs-io/babylon/testutil/datagen" + bbn "github.com/babylonlabs-io/babylon/types" + btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" + ftypes "github.com/babylonlabs-io/babylon/x/finality/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkquery "github.com/cosmos/cosmos-sdk/types/query" + sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/relayer/v2/relayer/provider" + "github.com/stretchr/testify/require" +) + +var ( + r = rand.New(rand.NewSource(time.Now().Unix())) + + // covenant + covenantSk, _ = btcec.PrivKeyFromBytes( + []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + ) +) + +func (tm *TestManager) getBTCUnbondingTime(t *testing.T) uint32 { + bsParams, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + + return bsParams.Params.UnbondingTimeBlocks +} + +func (tm *TestManager) CreateFinalityProvider(t *testing.T) (*bstypes.FinalityProvider, *btcec.PrivateKey) { + var err error + signerAddr := tm.BabylonClient.MustGetAddr() + addr := sdk.MustAccAddressFromBech32(signerAddr) + + fpSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcFp, err := datagen.GenRandomFinalityProviderWithBTCBabylonSKs(r, fpSK, addr) + require.NoError(t, err) + + /* + create finality provider + */ + commission := sdkmath.LegacyZeroDec() + msgNewVal := &bstypes.MsgCreateFinalityProvider{ + Addr: signerAddr, + Description: &stakingtypes.Description{Moniker: datagen.GenRandomHexStr(r, 10)}, + Commission: &commission, + BtcPk: btcFp.BtcPk, + Pop: btcFp.Pop, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgNewVal, nil, nil) + require.NoError(t, err) + + return btcFp, fpSK +} + +func (tm *TestManager) CreateBTCDelegation( + t *testing.T, + fpSK *btcec.PrivateKey, +) (*datagen.TestStakingSlashingInfo, *datagen.TestUnbondingSlashingInfo, *btcec.PrivateKey) { + signerAddr := tm.BabylonClient.MustGetAddr() + addr := sdk.MustAccAddressFromBech32(signerAddr) + + fpPK := fpSK.PubKey() + + /* + create BTC delegation + */ + // generate staking tx and slashing tx + bsParams, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + covenantBtcPks, err := bbnPksToBtcPks(bsParams.Params.CovenantPks) + require.NoError(t, err) + stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks + // get top UTXO + topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() + require.NoError(t, err) + topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) + require.NoError(t, err) + // staking value + stakingValue := int64(topUTXO.Amount) / 3 + + // generate legitimate BTC del + stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash := tm.createStakingAndSlashingTx(t, fpSK, bsParams, covenantBtcPks, topUTXO, stakingValue, stakingTimeBlocks) + + // send staking tx to Bitcoin node's mempool + _, err = tm.BTCClient.SendRawTransaction(stakingMsgTx, true) + require.NoError(t, err) + + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{stakingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineBlock(t) + require.Equal(t, 2, len(mBlock.Transactions)) + + // wait until staking tx is on Bitcoin + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(stakingMsgTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + // get spv proof of the BTC staking tx + stakingTxInfo := getTxInfo(t, mBlock) + + // insert k empty blocks to Bitcoin + btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() + require.NoError(t, err) + btccParams := btccParamsResp.Params + for i := 0; i < int(btccParams.BtcConfirmationDepth); i++ { + tm.mineBlock(t) + } + + stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) + require.NoError(t, err) + + // create PoP + pop, err := bstypes.NewPoPBTC(addr, tm.WalletPrivKey) + require.NoError(t, err) + slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + // generate proper delegator sig + require.NoError(t, err) + + delegatorSig, err := stakingSlashingInfo.SlashingTx.Sign( + stakingMsgTx, + stakingOutIdx, + slashingSpendPath.GetPkScriptPath(), + tm.WalletPrivKey, + ) + require.NoError(t, err) + + // Generate all data necessary for unbonding + unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( + t, + fpPK, + bsParams, + covenantBtcPks, + stakingSlashingInfo, + stakingMsgTxHash, + stakingOutIdx, + stakingTimeBlocks, + ) + + tm.CatchUpBTCLightClient(t) + + // Build a message to send + // submit BTC delegation to Babylon + msgBTCDel := &bstypes.MsgCreateBTCDelegation{ + StakerAddr: signerAddr, + Pop: pop, + BtcPk: bbn.NewBIP340PubKeyFromBTCPK(tm.WalletPrivKey.PubKey()), + FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, + StakingTime: stakingTimeBlocks, + StakingValue: stakingValue, + StakingTx: stakingTxInfo.Transaction, + StakingTxInclusionProof: &bstypes.InclusionProof{ + Key: stakingTxInfo.Key, + Proof: stakingTxInfo.Proof, + }, + SlashingTx: stakingSlashingInfo.SlashingTx, + DelegatorSlashingSig: delegatorSig, + // Unbonding related data + UnbondingTime: tm.getBTCUnbondingTime(t), + UnbondingTx: unbondingTxBytes, + UnbondingValue: unbondingSlashingInfo.UnbondingInfo.UnbondingOutput.Value, + UnbondingSlashingTx: unbondingSlashingInfo.SlashingTx, + DelegatorUnbondingSlashingSig: slashingTxSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgBTCDel, nil, nil) + require.NoError(t, err) + t.Logf("submitted MsgCreateBTCDelegation") + + // generate and insert new covenant signature, to activate the BTC delegation + tm.addCovenantSig( + t, + signerAddr, + stakingMsgTx, + stakingMsgTxHash, + fpSK, slashingSpendPath, + stakingSlashingInfo, + unbondingSlashingInfo, + unbondingSlashingPathSpendInfo, + stakingOutIdx, + ) + + return stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey +} + +func (tm *TestManager) CreateBTCDelegationWithoutIncl( + t *testing.T, + fpSK *btcec.PrivateKey, +) (*wire.MsgTx, *datagen.TestStakingSlashingInfo, *datagen.TestUnbondingSlashingInfo, *btcec.PrivateKey) { + signerAddr := tm.BabylonClient.MustGetAddr() + addr := sdk.MustAccAddressFromBech32(signerAddr) + + fpPK := fpSK.PubKey() + + /* + create BTC delegation + */ + // generate staking tx and slashing tx + bsParams, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + covenantBtcPks, err := bbnPksToBtcPks(bsParams.Params.CovenantPks) + require.NoError(t, err) + stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks + // get top UTXO + topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() + require.NoError(t, err) + topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) + require.NoError(t, err) + // staking value + stakingValue := int64(topUTXO.Amount) / 3 + + // generate legitimate BTC del + stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash := tm.createStakingAndSlashingTx(t, fpSK, bsParams, covenantBtcPks, topUTXO, stakingValue, stakingTimeBlocks) + + stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) + require.NoError(t, err) + + // create PoP + pop, err := bstypes.NewPoPBTC(addr, tm.WalletPrivKey) + require.NoError(t, err) + slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + // generate proper delegator sig + require.NoError(t, err) + + delegatorSig, err := stakingSlashingInfo.SlashingTx.Sign( + stakingMsgTx, + stakingOutIdx, + slashingSpendPath.GetPkScriptPath(), + tm.WalletPrivKey, + ) + require.NoError(t, err) + + // Generate all data necessary for unbonding + unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( + t, + fpPK, + bsParams, + covenantBtcPks, + stakingSlashingInfo, + stakingMsgTxHash, + stakingOutIdx, + stakingTimeBlocks, + ) + + var stakingTxBuf bytes.Buffer + err = stakingMsgTx.Serialize(&stakingTxBuf) + require.NoError(t, err) + + // submit BTC delegation to Babylon + msgBTCDel := &bstypes.MsgCreateBTCDelegation{ + StakerAddr: signerAddr, + Pop: pop, + BtcPk: bbn.NewBIP340PubKeyFromBTCPK(tm.WalletPrivKey.PubKey()), + FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, + StakingTime: stakingTimeBlocks, + StakingValue: stakingValue, + StakingTx: stakingTxBuf.Bytes(), + StakingTxInclusionProof: nil, + SlashingTx: stakingSlashingInfo.SlashingTx, + DelegatorSlashingSig: delegatorSig, + // Unbonding related data + UnbondingTime: uint32(tm.getBTCUnbondingTime(t)), + UnbondingTx: unbondingTxBytes, + UnbondingValue: unbondingSlashingInfo.UnbondingInfo.UnbondingOutput.Value, + UnbondingSlashingTx: unbondingSlashingInfo.SlashingTx, + DelegatorUnbondingSlashingSig: slashingTxSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgBTCDel, nil, nil) + require.NoError(t, err) + t.Logf("submitted MsgCreateBTCDelegation") + + // generate and insert new covenant signature, to activate the BTC delegation + tm.addCovenantSig( + t, + signerAddr, + stakingMsgTx, + stakingMsgTxHash, + fpSK, slashingSpendPath, + stakingSlashingInfo, + unbondingSlashingInfo, + unbondingSlashingPathSpendInfo, + stakingOutIdx, + ) + + return stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey +} + +func (tm *TestManager) createStakingAndSlashingTx( + t *testing.T, fpSK *btcec.PrivateKey, + bsParams *bstypes.QueryParamsResponse, + covenantBtcPks []*btcec.PublicKey, + topUTXO *types.UTXO, + stakingValue int64, + stakingTimeBlocks uint32, +) (*wire.MsgTx, *datagen.TestStakingSlashingInfo, *chainhash.Hash) { + // generate staking tx and slashing tx + fpPK := fpSK.PubKey() + + // generate legitimate BTC del + stakingSlashingInfo := datagen.GenBTCStakingSlashingInfoWithOutPoint( + r, + t, + regtestParams, + topUTXO.GetOutPoint(), + tm.WalletPrivKey, + []*btcec.PublicKey{fpPK}, + covenantBtcPks, + bsParams.Params.CovenantQuorum, + uint16(stakingTimeBlocks), + stakingValue, + bsParams.Params.SlashingPkScript, + bsParams.Params.SlashingRate, + uint16(tm.getBTCUnbondingTime(t)), + ) + // sign staking tx and overwrite the staking tx to the signed version + // NOTE: the tx hash has changed here since stakingMsgTx is pre-segwit + stakingMsgTx, signed, err := tm.BTCClient.SignRawTransactionWithWallet(stakingSlashingInfo.StakingTx) + require.NoError(t, err) + require.True(t, signed) + // overwrite staking tx + stakingSlashingInfo.StakingTx = stakingMsgTx + // get signed staking tx hash + stakingMsgTxHash1 := stakingSlashingInfo.StakingTx.TxHash() + stakingMsgTxHash := &stakingMsgTxHash1 + t.Logf("signed staking tx hash: %s", stakingMsgTxHash.String()) + + // change outpoint tx hash of slashing tx to the txhash of the signed staking tx + slashingMsgTx, err := stakingSlashingInfo.SlashingTx.ToMsgTx() + require.NoError(t, err) + slashingMsgTx.TxIn[0].PreviousOutPoint.Hash = stakingSlashingInfo.StakingTx.TxHash() + // update slashing tx + stakingSlashingInfo.SlashingTx, err = bstypes.NewBTCSlashingTxFromMsgTx(slashingMsgTx) + require.NoError(t, err) + + return stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash +} + +func (tm *TestManager) createUnbondingData( + t *testing.T, + fpPK *btcec.PublicKey, + bsParams *bstypes.QueryParamsResponse, + covenantBtcPks []*btcec.PublicKey, + stakingSlashingInfo *datagen.TestStakingSlashingInfo, + stakingMsgTxHash *chainhash.Hash, + stakingOutIdx uint32, + stakingTimeBlocks uint32, +) (*datagen.TestUnbondingSlashingInfo, *btcstaking.SpendInfo, []byte, *bbn.BIP340Signature) { + fee := int64(1000) + unbondingValue := stakingSlashingInfo.StakingInfo.StakingOutput.Value - fee + unbondingSlashingInfo := datagen.GenBTCUnbondingSlashingInfo( + r, + t, + regtestParams, + tm.WalletPrivKey, + []*btcec.PublicKey{fpPK}, + covenantBtcPks, + bsParams.Params.CovenantQuorum, + wire.NewOutPoint(stakingMsgTxHash, stakingOutIdx), + uint16(stakingTimeBlocks), + unbondingValue, + bsParams.Params.SlashingPkScript, + bsParams.Params.SlashingRate, + uint16(tm.getBTCUnbondingTime(t)), + ) + unbondingTxBytes, err := bbn.SerializeBTCTx(unbondingSlashingInfo.UnbondingTx) + require.NoError(t, err) + + unbondingSlashingPathSpendInfo, err := unbondingSlashingInfo.UnbondingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + slashingTxSig, err := unbondingSlashingInfo.SlashingTx.Sign( + unbondingSlashingInfo.UnbondingTx, + 0, // Only one output in the unbonding tx + unbondingSlashingPathSpendInfo.GetPkScriptPath(), + tm.WalletPrivKey, + ) + require.NoError(t, err) + + return unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig +} + +func (tm *TestManager) addCovenantSig( + t *testing.T, + signerAddr string, + stakingMsgTx *wire.MsgTx, + stakingMsgTxHash *chainhash.Hash, + fpSK *btcec.PrivateKey, + slashingSpendPath *btcstaking.SpendInfo, + stakingSlashingInfo *datagen.TestStakingSlashingInfo, + unbondingSlashingInfo *datagen.TestUnbondingSlashingInfo, + unbondingSlashingPathSpendInfo *btcstaking.SpendInfo, + stakingOutIdx uint32, +) { + // TODO: Make this handle multiple covenant signatures + fpEncKey, err := asig.NewEncryptionKeyFromBTCPK(fpSK.PubKey()) + require.NoError(t, err) + covenantSig, err := stakingSlashingInfo.SlashingTx.EncSign( + stakingMsgTx, + stakingOutIdx, + slashingSpendPath.GetPkScriptPath(), + covenantSk, + fpEncKey, + ) + require.NoError(t, err) + // TODO: Add covenant sigs for all covenants + // add covenant sigs + // covenant Schnorr sig on unbonding tx + unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + unbondingTxCovenantSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingSlashingInfo.UnbondingTx, + stakingSlashingInfo.StakingTx, + stakingOutIdx, + unbondingPathSpendInfo.GetPkScriptPath(), + covenantSk, + ) + require.NoError(t, err) + covenantUnbondingSig := bbn.NewBIP340SignatureFromBTCSig(unbondingTxCovenantSchnorrSig) + // covenant adaptor sig on unbonding slashing tx + require.NoError(t, err) + covenantSlashingSig, err := unbondingSlashingInfo.SlashingTx.EncSign( + unbondingSlashingInfo.UnbondingTx, + 0, // Only one output in the unbonding transaction + unbondingSlashingPathSpendInfo.GetPkScriptPath(), + covenantSk, + fpEncKey, + ) + require.NoError(t, err) + msgAddCovenantSig := &bstypes.MsgAddCovenantSigs{ + Signer: signerAddr, + Pk: bbn.NewBIP340PubKeyFromBTCPK(covenantSk.PubKey()), + StakingTxHash: stakingMsgTxHash.String(), + SlashingTxSigs: [][]byte{covenantSig.MustMarshal()}, + UnbondingTxSig: covenantUnbondingSig, + SlashingUnbondingTxSigs: [][]byte{covenantSlashingSig.MustMarshal()}, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgAddCovenantSig, nil, nil) + require.NoError(t, err) + t.Logf("submitted covenant signature") +} + +func (tm *TestManager) Undelegate( + t *testing.T, + stakingSlashingInfo *datagen.TestStakingSlashingInfo, + unbondingSlashingInfo *datagen.TestUnbondingSlashingInfo, + delSK *btcec.PrivateKey, + catchUpLightClientFunc func()) (*datagen.TestUnbondingSlashingInfo, *schnorr.Signature) { + signerAddr := tm.BabylonClient.MustGetAddr() + + // TODO: This generates unbonding tx signature, move it to undelegate + unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + + // the only input to unbonding tx is the staking tx + stakingOutIdx, err := outIdx(unbondingSlashingInfo.UnbondingTx, unbondingSlashingInfo.UnbondingInfo.UnbondingOutput) + require.NoError(t, err) + unbondingTxSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingSlashingInfo.UnbondingTx, + stakingSlashingInfo.StakingTx, + stakingOutIdx, + unbondingPathSpendInfo.GetPkScriptPath(), + delSK, + ) + require.NoError(t, err) + + var unbondingTxBuf bytes.Buffer + err = unbondingSlashingInfo.UnbondingTx.Serialize(&unbondingTxBuf) + require.NoError(t, err) + + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + covenantSigs := resp.BtcDelegation.UndelegationResponse.CovenantUnbondingSigList + witness, err := unbondingPathSpendInfo.CreateUnbondingPathWitness( + []*schnorr.Signature{covenantSigs[0].Sig.MustToBTCSig()}, + unbondingTxSchnorrSig, + ) + require.NoError(t, err) + unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness + + // send unbonding tx to Bitcoin node's mempool + unbondingTxHash, err := tm.BTCClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) + require.NoError(t, err) + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(unbondingTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + t.Logf("submitted unbonding tx with hash %s", unbondingTxHash.String()) + + // mine a block with this tx, and insert it to Bitcoin + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineBlock(t) + require.Equal(t, 2, len(mBlock.Transactions)) + + catchUpLightClientFunc() + + unbondingTxInfo := getTxInfo(t, mBlock) + msgUndel := &bstypes.MsgBTCUndelegate{ + Signer: signerAddr, + StakingTxHash: stakingSlashingInfo.StakingTx.TxHash().String(), + StakeSpendingTx: unbondingTxBuf.Bytes(), + StakeSpendingTxInclusionProof: &bstypes.InclusionProof{ + Key: unbondingTxInfo.Key, + Proof: unbondingTxInfo.Proof, + }, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgUndel, nil, nil) + require.NoError(t, err) + t.Logf("submitted MsgBTCUndelegate") + + // wait until unbonding tx is on Bitcoin + require.Eventually(t, func() bool { + resp, err := tm.BTCClient.GetRawTransactionVerbose(unbondingTxHash) + if err != nil { + t.Logf("err of GetRawTransactionVerbose: %v", err) + return false + } + return len(resp.BlockHash) > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + return unbondingSlashingInfo, unbondingTxSchnorrSig +} + +func (tm *TestManager) VoteAndEquivocate(t *testing.T, fpSK *btcec.PrivateKey) { + signerAddr := tm.BabylonClient.MustGetAddr() + + // get the finality provider + fpBTCPK := bbn.NewBIP340PubKeyFromBTCPK(fpSK.PubKey()) + fpResp, err := tm.BabylonClient.FinalityProvider(fpBTCPK.MarshalHex()) + require.NoError(t, err) + btcFp := fpResp.FinalityProvider + + _, err = tm.BabylonClient.ActivatedHeight() + require.Error(t, err) + + activatedHeight := uint64(1) + commitStartHeight := activatedHeight + + /* + commit a number of public randomness since activatedHeight + */ + srList, msgCommitPubRandList, err := datagen.GenRandomMsgCommitPubRandList(r, fpSK, activatedHeight, 100) + require.NoError(t, err) + msgCommitPubRandList.Signer = signerAddr + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgCommitPubRandList, nil, nil) + require.NoError(t, err) + t.Logf("committed public randomness") + + tm.mineBlock(t) + + tm.waitForFpPubRandTimestamped(t, fpSK.PubKey()) + + require.Eventually(t, func() bool { + acr, err := tm.BabylonClient.ActivatedHeight() + if err != nil { + return false + } + activatedHeight = acr.Height + return activatedHeight > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + /* + submit finality signature + */ + // get block to vote + blockToVote, err := tm.BabylonClient.GetBlock(int64(activatedHeight)) + require.NoError(t, err) + msgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), blockToVote.Block.AppHash...) + // generate EOTS signature + idx := activatedHeight - commitStartHeight + sig, err := eots.Sign(fpSK, srList.SRList[idx], msgToSign) + require.NoError(t, err) + eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig) + // submit finality signature + msgAddFinalitySig := &ftypes.MsgAddFinalitySig{ + Signer: signerAddr, + FpBtcPk: btcFp.BtcPk, + BlockHeight: activatedHeight, + PubRand: &srList.PRList[idx], + Proof: srList.ProofList[idx].ToProto(), + BlockAppHash: blockToVote.Block.AppHash, + FinalitySig: eotsSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgAddFinalitySig, nil, nil) + require.NoError(t, err) + t.Logf("submitted finality signature") + + /* + equivocate + */ + invalidAppHash := datagen.GenRandomByteArray(r, 32) + invalidMsgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), invalidAppHash...) + invalidSig, err := eots.Sign(fpSK, srList.SRList[idx], invalidMsgToSign) + require.NoError(t, err) + invalidEotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(invalidSig) + invalidMsgAddFinalitySig := &ftypes.MsgAddFinalitySig{ + Signer: signerAddr, + FpBtcPk: btcFp.BtcPk, + BlockHeight: activatedHeight, + PubRand: &srList.PRList[idx], + Proof: srList.ProofList[idx].ToProto(), + BlockAppHash: invalidAppHash, + FinalitySig: invalidEotsSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), invalidMsgAddFinalitySig, nil, nil) + require.NoError(t, err) + t.Logf("submitted equivocating finality signature") +} + +func getTxInfo(t *testing.T, block *wire.MsgBlock) *btcctypes.TransactionInfo { + mHeaderBytes := bbn.NewBTCHeaderBytesFromBlockHeader(&block.Header) + var txBytes [][]byte + for _, tx := range block.Transactions { + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + _ = tx.Serialize(buf) + txBytes = append(txBytes, buf.Bytes()) + } + spvProof, err := btcctypes.SpvProofFromHeaderAndTransactions(&mHeaderBytes, txBytes, 1) + require.NoError(t, err) + return btcctypes.NewTransactionInfoFromSpvProof(spvProof) +} + +// TODO: these functions should be enabled by Babylon +func bbnPksToBtcPks(pks []bbn.BIP340PubKey) ([]*btcec.PublicKey, error) { + btcPks := make([]*btcec.PublicKey, 0, len(pks)) + for _, pk := range pks { + btcPk, err := pk.ToBTCPK() + if err != nil { + return nil, err + } + btcPks = append(btcPks, btcPk) + } + return btcPks, nil +} + +func outIdx(tx *wire.MsgTx, candOut *wire.TxOut) (uint32, error) { + for idx, out := range tx.TxOut { + if bytes.Equal(out.PkScript, candOut.PkScript) && out.Value == candOut.Value { + return uint32(idx), nil + } + } + return 0, fmt.Errorf("couldn't find output") +} + +func (tm *TestManager) waitForFpPubRandTimestamped(t *testing.T, fpPk *btcec.PublicKey) { + var lastCommittedHeight uint64 + var err error + + require.Eventually(t, func() bool { + lastCommittedHeight, err = tm.getLastCommittedHeight(fpPk) + if err != nil { + return false + } + return lastCommittedHeight > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("public randomness is successfully committed, last committed height: %d", lastCommittedHeight) + + // wait until the last registered epoch is finalized + currentEpoch, err := tm.BabylonClient.CurrentEpoch() + require.NoError(t, err) + + tm.finalizeUntilEpoch(t, currentEpoch.CurrentEpoch) + + res, err := tm.BabylonClient.LatestEpochFromStatus(ckpttypes.Finalized) + require.NoError(t, err) + t.Logf("last finalized epoch: %d", res.RawCheckpoint.EpochNum) + + t.Logf("public randomness is successfully timestamped, last finalized epoch: %v", currentEpoch) +} + +// queryLastCommittedPublicRand returns the last public randomness commitments +func (tm *TestManager) queryLastCommittedPublicRand(fpPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { + fpBtcPk := bbn.NewBIP340PubKeyFromBTCPK(fpPk) + + pagination := &sdkquery.PageRequest{ + Limit: count, + Reverse: true, + } + + res, err := tm.BabylonClient.QueryClient.ListPubRandCommit(fpBtcPk.MarshalHex(), pagination) + if err != nil { + return nil, fmt.Errorf("failed to query committed public randomness: %w", err) + } + + return res.PubRandCommitMap, nil +} + +func (tm *TestManager) lastCommittedPublicRandWithRetry(btcPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { + var response map[uint64]*ftypes.PubRandCommitResponse + + if err := retry.Do(func() error { + resp, err := tm.queryLastCommittedPublicRand(btcPk, count) + if err != nil { + return err + } + response = resp + return nil + }, + retry.Attempts(tm.Config.Common.MaxRetryTimes), + retry.Delay(tm.Config.Common.RetrySleepTime), + retry.LastErrorOnly(true)); err != nil { + return nil, err + } + + return response, nil +} + +func (tm *TestManager) getLastCommittedHeight(btcPk *btcec.PublicKey) (uint64, error) { + pubRandCommitMap, err := tm.lastCommittedPublicRandWithRetry(btcPk, 1) + if err != nil { + return 0, err + } + + // no committed randomness yet + if len(pubRandCommitMap) == 0 { + return 0, nil + } + + if len(pubRandCommitMap) > 1 { + return 0, fmt.Errorf("got more than one last committed public randomness") + } + var lastCommittedHeight uint64 + for startHeight, resp := range pubRandCommitMap { + lastCommittedHeight = startHeight + resp.NumPubRand - 1 + } + + return lastCommittedHeight, nil +} + +func (tm *TestManager) finalizeUntilEpoch(t *testing.T, epoch uint64) { + bbnClient := tm.BabylonClient + + // wait until the checkpoint of this epoch is sealed + require.Eventually(t, func() bool { + lastSealedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Sealed) + if err != nil { + return false + } + return epoch <= lastSealedCkpt.RawCheckpoint.EpochNum + }, eventuallyWaitTimeOut, 1*time.Second) + + t.Logf("start finalizing epochs until %d", epoch) + // Random source for the generation of BTC data + r := rand.New(rand.NewSource(time.Now().Unix())) + + // get all checkpoints of these epochs + pagination := &sdkquerytypes.PageRequest{ + Key: ckpttypes.CkptsObjectKey(0), + Limit: epoch, + } + resp, err := bbnClient.RawCheckpoints(pagination) + require.NoError(t, err) + require.Equal(t, int(epoch), len(resp.RawCheckpoints)) + + submitterAddr, err := sdk.AccAddressFromBech32(tm.BabylonClient.MustGetAddr()) + require.NoError(t, err) + + for _, checkpoint := range resp.RawCheckpoints { + currentBtcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() + require.NoError(t, err) + tipHeader, err := bbn.NewBTCHeaderBytesFromHex(currentBtcTipResp.Header.HeaderHex) + require.NoError(t, err) + + rawCheckpoint, err := checkpoint.Ckpt.ToRawCheckpoint() + require.NoError(t, err) + + btcCheckpoint, err := ckpttypes.FromRawCkptToBTCCkpt(rawCheckpoint, submitterAddr) + require.NoError(t, err) + + babylonTagBytes, err := hex.DecodeString("01020304") + require.NoError(t, err) + + p1, p2, err := txformat.EncodeCheckpointData( + babylonTagBytes, + txformat.CurrentVersion, + btcCheckpoint, + ) + require.NoError(t, err) + + tx1 := datagen.CreatOpReturnTransaction(r, p1) + + opReturn1 := datagen.CreateBlockWithTransaction(r, tipHeader.ToBlockHeader(), tx1) + tx2 := datagen.CreatOpReturnTransaction(r, p2) + opReturn2 := datagen.CreateBlockWithTransaction(r, opReturn1.HeaderBytes.ToBlockHeader(), tx2) + + // insert headers and proofs + _, err = tm.insertBtcBlockHeaders([]bbn.BTCHeaderBytes{ + opReturn1.HeaderBytes, + opReturn2.HeaderBytes, + }) + require.NoError(t, err) + + _, err = tm.insertSpvProofs(submitterAddr.String(), []*btcctypes.BTCSpvProof{ + opReturn1.SpvProof, + opReturn2.SpvProof, + }) + require.NoError(t, err) + + // wait until this checkpoint is submitted + require.Eventually(t, func() bool { + ckpt, err := bbnClient.RawCheckpoint(checkpoint.Ckpt.EpochNum) + if err != nil { + return false + } + return ckpt.RawCheckpoint.Status == ckpttypes.Submitted + }, eventuallyWaitTimeOut, eventuallyPollTime) + } + + // insert w BTC headers + tm.insertWBTCHeaders(t, r) + + // wait until the checkpoint of this epoch is finalised + require.Eventually(t, func() bool { + lastFinalizedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Finalized) + if err != nil { + t.Logf("failed to get last finalized epoch: %v", err) + return false + } + return epoch <= lastFinalizedCkpt.RawCheckpoint.EpochNum + }, eventuallyWaitTimeOut, 1*time.Second) + + t.Logf("epoch %d is finalised", epoch) +} + +func (tm *TestManager) insertBtcBlockHeaders(headers []bbn.BTCHeaderBytes) (*provider.RelayerTxResponse, error) { + msg := &btclctypes.MsgInsertHeaders{ + Signer: tm.MustGetBabylonSigner(), + Headers: headers, + } + + res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) + if err != nil { + return nil, err + } + + return res, nil +} + +func (tm *TestManager) insertSpvProofs(submitter string, proofs []*btcctypes.BTCSpvProof) (*provider.RelayerTxResponse, error) { + msg := &btcctypes.MsgInsertBTCSpvProof{ + Submitter: submitter, + Proofs: proofs, + } + + res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) + if err != nil { + return nil, err + } + + return res, nil +} + +func (tm *TestManager) insertWBTCHeaders(t *testing.T, r *rand.Rand) { + ckptParamRes, err := tm.BabylonClient.QueryClient.BTCCheckpointParams() + require.NoError(t, err) + btcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() + require.NoError(t, err) + tipHeader, err := bbn.NewBTCHeaderBytesFromHex(btcTipResp.Header.HeaderHex) + require.NoError(t, err) + kHeaders := datagen.NewBTCHeaderChainFromParentInfo(r, &btclctypes.BTCHeaderInfo{ + Header: &tipHeader, + Hash: tipHeader.Hash(), + Height: btcTipResp.Header.Height, + Work: &btcTipResp.Header.Work, + }, uint32(ckptParamRes.Params.CheckpointFinalizationTimeout)) + _, err = tm.insertBtcBlockHeaders(kHeaders.ChainToBytes()) + require.NoError(t, err) +} diff --git a/e2etest/unbondingwatcher_e2e_test.go b/e2etest/unbondingwatcher_e2e_test.go new file mode 100644 index 0000000..80cbde8 --- /dev/null +++ b/e2etest/unbondingwatcher_e2e_test.go @@ -0,0 +1,336 @@ +//go:build e2e +// +build e2e + +package e2etest + +import ( + "fmt" + btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + promtestutil "github.com/prometheus/client_golang/prometheus/testutil" + "go.uber.org/zap" + "sync" + "testing" + "time" + + "github.com/babylonlabs-io/babylon/btcstaking" + "github.com/babylonlabs-io/vigilante/btcclient" + bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" + "github.com/babylonlabs-io/vigilante/config" + "github.com/babylonlabs-io/vigilante/metrics" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/stretchr/testify/require" +) + +func TestUnbondingWatcher(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's needed by staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + emptyHintCache := btcclient.EmptyHintCache{} + + backend, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &emptyHintCache, + ) + require.NoError(t, err) + + err = backend.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + backend, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + bsTracker.Start() + defer bsTracker.Stop() + + // set up a finality provider + _, fpSK := tm.CreateFinalityProvider(t) + // set up a BTC delegation + stakingSlashingInfo, unbondingSlashingInfo, delSK := tm.CreateBTCDelegation(t, fpSK) + + // Staker unbonds by directly sending tx to btc network. Watcher should detect it and report to babylon. + unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + stakingOutIdx, err := outIdx(unbondingSlashingInfo.UnbondingTx, unbondingSlashingInfo.UnbondingInfo.UnbondingOutput) + require.NoError(t, err) + + unbondingTxSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingSlashingInfo.UnbondingTx, + stakingSlashingInfo.StakingTx, + stakingOutIdx, + unbondingPathSpendInfo.GetPkScriptPath(), + delSK, + ) + require.NoError(t, err) + + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + + covenantSigs := resp.BtcDelegation.UndelegationResponse.CovenantUnbondingSigList + witness, err := unbondingPathSpendInfo.CreateUnbondingPathWitness( + []*schnorr.Signature{covenantSigs[0].Sig.MustToBTCSig()}, + unbondingTxSchnorrSig, + ) + unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness + + // Send unbonding tx to Bitcoin + _, err = tm.BTCClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) + require.NoError(t, err) + + // mine a block with this tx, and insert it to Bitcoin + unbondingTxHash := unbondingSlashingInfo.UnbondingTx.TxHash() + t.Logf("submitted unbonding tx with hash %s", unbondingTxHash.String()) + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&unbondingTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + minedBlock := tm.mineBlock(t) + require.Equal(t, 2, len(minedBlock.Transactions)) + + tm.CatchUpBTCLightClient(t) + + require.Eventually(t, func() bool { + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + + // TODO: Add field for staker signature in BTCDelegation query to check it directly, + // for now it is enough to check that delegation is not active, as if unbonding was reported + // delegation will be deactivated + return !resp.BtcDelegation.Active + + }, eventuallyWaitTimeOut, eventuallyPollTime) +} + +// TestActivatingDelegation verifies that a delegation created without an inclusion proof will +// eventually become "active". +// Specifically, that stakingEventWatcher will send a MsgAddBTCDelegationInclusionProof to do so. +func TestActivatingDelegation(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's necessary for staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + btcNotifier, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &btcclient.EmptyHintCache{}, + ) + require.NoError(t, err) + + err = btcNotifier.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + btcNotifier, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + bsTracker.Start() + defer bsTracker.Stop() + + // set up a finality provider + _, fpSK := tm.CreateFinalityProvider(t) + // set up a BTC delegation + stakingMsgTx, stakingSlashingInfo, _, _ := tm.CreateBTCDelegationWithoutIncl(t, fpSK) + stakingMsgTxHash := stakingMsgTx.TxHash() + + // send staking tx to Bitcoin node's mempool + _, err = tm.BTCClient.SendRawTransaction(stakingMsgTx, true) + require.NoError(t, err) + + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&stakingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineBlock(t) + require.Equal(t, 2, len(mBlock.Transactions)) + + // wait until staking tx is on Bitcoin + require.Eventually(t, func() bool { + _, err := tm.BTCClient.GetRawTransaction(&stakingMsgTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + // We want to introduce a latency to make sure that we are not trying to submit inclusion proof while the + // staking tx is not yet K-deep + time.Sleep(10 * time.Second) + // Insert k empty blocks to Bitcoin + btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() + if err != nil { + fmt.Println("Error fetching BTCCheckpointParams:", err) + return + } + for i := 0; i < int(btccParamsResp.Params.BtcConfirmationDepth); i++ { + tm.mineBlock(t) + } + tm.CatchUpBTCLightClient(t) + }() + + wg.Wait() + + // make sure we didn't submit any "invalid" incl proof + require.Eventually(t, func() bool { + return promtestutil.ToFloat64(stakingTrackerMetrics.FailedReportedActivateDelegations) == 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // created delegation lacks inclusion proof, once created it will be in + // pending status, once convenant signatures are added it will be in verified status, + // and once the stakingEventWatcher submits MsgAddBTCDelegationInclusionProof it will + // be in active status + require.Eventually(t, func() bool { + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + + return resp.BtcDelegation.Active + }, eventuallyWaitTimeOut, eventuallyPollTime) +} + +// TestActivatingAndUnbondingDelegation tests that delegation will eventually become UNBONDED given that +// both staking and unbonding tx are in the same block. +// In this test, we include both staking tx and unbonding tx in the same block. +// The delegation goes through "VERIFIED" → "ACTIVE" → "UNBONDED" status throughout this test. +func TestActivatingAndUnbondingDelegation(t *testing.T) { + //t.Parallel() + // segwit is activated at height 300. It's necessary for staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + + btcNotifier, err := btcclient.NewNodeBackend( + btcclient.ToBitcoindConfig(tm.Config.BTC), + &chaincfg.RegressionNetParams, + &btcclient.EmptyHintCache{}, + ) + require.NoError(t, err) + + err = btcNotifier.Start() + require.NoError(t, err) + + commonCfg := config.DefaultCommonConfig() + bstCfg := config.DefaultBTCStakingTrackerConfig() + bstCfg.CheckDelegationsInterval = 1 * time.Second + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() + + bsTracker := bst.NewBTCStakingTracker( + tm.BTCClient, + btcNotifier, + tm.BabylonClient, + &bstCfg, + &commonCfg, + zap.NewNop(), + stakingTrackerMetrics, + ) + bsTracker.Start() + defer bsTracker.Stop() + + // set up a finality provider + _, fpSK := tm.CreateFinalityProvider(t) + // set up a BTC delegation + stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, delSK := tm.CreateBTCDelegationWithoutIncl(t, fpSK) + stakingMsgTxHash := stakingMsgTx.TxHash() + + // send staking tx to Bitcoin node's mempool + _, err = tm.BTCClient.SendRawTransaction(stakingMsgTx, true) + require.NoError(t, err) + + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&stakingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // Staker unbonds by directly sending tx to btc network. Watcher should detect it and report to babylon. + unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + stakingOutIdx, err := outIdx(unbondingSlashingInfo.UnbondingTx, unbondingSlashingInfo.UnbondingInfo.UnbondingOutput) + require.NoError(t, err) + + unbondingTxSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingSlashingInfo.UnbondingTx, + stakingSlashingInfo.StakingTx, + stakingOutIdx, + unbondingPathSpendInfo.GetPkScriptPath(), + delSK, + ) + require.NoError(t, err) + + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + + covenantSigs := resp.BtcDelegation.UndelegationResponse.CovenantUnbondingSigList + witness, err := unbondingPathSpendInfo.CreateUnbondingPathWitness( + []*schnorr.Signature{covenantSigs[0].Sig.MustToBTCSig()}, + unbondingTxSchnorrSig, + ) + require.NoError(t, err) + unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness + + // Send unbonding tx to Bitcoin + _, err = tm.BTCClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) + require.NoError(t, err) + + unbondingTxHash := unbondingSlashingInfo.UnbondingTx.TxHash() + t.Logf("submitted unbonding tx with hash %s", unbondingTxHash.String()) + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&unbondingTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineBlock(t) + // both staking and unbonding txs are in this block + require.Equal(t, 3, len(mBlock.Transactions)) + + // insert k empty blocks to Bitcoin + btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() + require.NoError(t, err) + btccParams := btccParamsResp.Params + for i := 0; i < int(btccParams.BtcConfirmationDepth); i++ { + tm.mineBlock(t) + } + + tm.CatchUpBTCLightClient(t) + + // wait until delegation has become unbonded + require.Eventually(t, func() bool { + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + + return resp.BtcDelegation.StatusDesc == btcstakingtypes.BTCDelegationStatus_UNBONDED.String() + }, eventuallyWaitTimeOut, eventuallyPollTime) +} diff --git a/e2etest/wallet.db b/e2etest/wallet.db new file mode 100644 index 0000000000000000000000000000000000000000..c8e7845e5b663b71dd86f588fc22a1fe4f8e6b1c GIT binary patch literal 196608 zcmeI*c|28H{|E4s;UIHLDIrDDV2C6cBFR*mOHqd-ncd@@%q3SBl~j~UPlybKQrAT! zN{LcYHEk^J_?>7Jjb*Yn)xdA;uQ$GxBRdab?IcdfPeKKp&P^WS%&P(}*R zXxsimsGW=K8;lGHjx^tnL!(7#bYyrf(cjtFu&_4lR>`-8=s*Ah5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1R(H-1;&l8{BQIBxkt|rRjt%(TwVD358oIIApijgKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5cnp*HQE#VHi8ZWAOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=|7!v6(HpY&0r+3vANPX*1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG{VLg5)j;Gzf&_r?*&BSa;~bBKV9b%@OY$l2{gCF3X}7UYQAiI`k3 zM^tit9I>eYIZwu9ktSj)(Z&o%6iONq^Nz;kdQIYF@_c=wlJPDgAnzwfRGPg#lW4wM zxG2BxS|CTPAg{5CyZ}+`h?b0J3`fMllo%jCz>u8eretB0X_S|%ZA|GeooA+)wnfWF zU-OgyD3LKwp{CGo#iuhlRqE~fn#ILy9&RgQE<7V|98;{>E*WaS+|X`A5X9G)q!aT zJ1ptSLLnEnpGp$VvsIKX0Th!)Itky!1mzw+dY7qserJXfTs zV=nqugPy6fi2H)%y%qZtvP(k5Uos<`xz-3Jb~o#KJ`s7bZC|3JM0h{dKh}Fwjr8Iu z{brN1H63m4{j1mNUwsvmIk~z|ZlCi5hk#V;W7;8n(b}&Ba@)N8YMl>u=o24~WjF1k zI1j`4-bTd_Hs#06O!ZWPDpDEJ=NG%VJhpRIYw~_l`>}j>l;SkS!d;?sa9VU!AOQ`=k%cs@)uz zwC{ZfJuaTAid}M#0|2%^c2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SNqQ0%RUOS^hTp{}MI<2UN@q7l{X^T>I6xe|yVG zyQ&!qB?H2}>M06Tmr~lbs`In2N4FT7Q9c>VxbHF#@~*n;)B1jk*aEtP_GaOtX%E-x z%(FLkol#b^;>L}~Nt9DzP9L~#&YX2ROnFhMT*S}uLB&cJR&S}H%(C208(JCebbjw9 z!?e?iFU8A#Oh~;?+1!5bZO|{tB3~TR%Fhj&$R9hpqx%Ax|9>#9CD& z#hOr7nMIm+$i!QHvJQ&u8j{!fpfRxbhOG8esrqpSKkF_Q-Jtz(-BbD{!QEAJi*ww~ z^gVQVC*BX9-e+old1lkR#jkH0bPCmQPoByrxSlr8)KcC>wWx7sqjHhFOOe+4UfT@M zLi2w6yKz3r{(XW*`&7p*v`WxCGt+3TvCU#nt7W$wWp{S35eOn*53+D1$0PrLgoNYB z&KLiW^Z$9a_1g8@q}~j&U8%NiS?c5T!urkgw>fQ{;B$0l;a%;Iu9f~5W2#aEx~}_8lFk`t$z#2|g!}#xKzHQ?@=3am*@Qi#znQ zqT#d0JDOeuThFmw)hj>OUTuq2)U4;1uFfh9E;i@=Nv1*A>!+{A4O@jMi!T}|yhv%! z50W@(({Q-6VL1OkSF?Wa8OFrz+c!=2_vD+>-*GC;rSN%9km>79m-HgNZNKpL|4?Ty zQQa_9D55vB=0oFIjnA|7LqZ)+#HGF9vtjg{Z#xy+RS|mc%~KbnlSe9^Ym^+db2dv8 zyu)R5+A97qoo8r%{@(=3?UfvJb~o9GTHVgdFKSJ1Zi+YY zQY^h*uwMIps77oN^8YzrJai!d0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*#kN&?i;hxyy& z|1;(E9aUnCx~V+r2{JRwH~K5Ru~oYAW3HZRy>!y4Cc#BQqQ^9k-*LI4XDxL@dTyt4 zTt}R?MSaQ&$yX~&x8QxZ`?SJux)J zOZ}|swRw5&6-8-byYGL>eJAFe>wEZB?u5lBeqiihCU;srj-gr@kyP77ZOZq|m26Qm zQmM9BQL3})eh)pnBd47A)Ww@;E{R`o)3|lqR;#owc-@2ai?2`EmL)jKTEuy*xqWR% z+^(gq?AIOn|731ITVrhdU+4de52l%0P0aHw`(pV{VedeCs;$0ozr`T4Xm7Nsw4UQR z)e}973g^32oOH347)bD2Bk-F&WJWDEGbBG*?JU+=S%x82euZ@!O3JyuXbFHH8xj61nh z;atn~`8wZqc` zZk6fLB+7&jT%_hX$;4ay<~8@Lt5SEobVf2*^Q4{ckZ9V+!aVIal9I3D$(ytY3M3w&+DAk*;y+r)YulhL~ZxA}$HjKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P-m6Nxv`a({rm#A0%NH&NYaPVPkW z-NHo~|7|>;=#keX_ZA>aEm6s9uwyCW!es70JKjvhtNYY-#b>Ap zG6!Q81=>ZiNTg5BmZ*+!eAnYHy}>*D$BI22_Fnc3Cr@Vb?dADW27?j2$_`{3mYrM#K^rDVChcT&9{b#A?9g8Mx-zjZq zufF0R!D7+N2i^)?zMFOW9qV!-*Ja~mAAXxiA+fXw-Z>9e&P@}4rOrE5`yw?Uz&iib zrv-bi*T4D6c1?HtRNEELy{x$MQ}-!OtF{x~qh0NNm&NR!>TgMYI+k5hH*5WRKV5Bp ztK`fVt`40U1GVp}cf9`48D&r9%~2^58vMX7zUfqDWz*_IXZwFinY(uSs>0Ad#prw; zkx6Fbqo}!^gQw-(eO+=b$J;o}kh*x@^2U?`#j-EQwJcUm{fD~ll{xPZNLzH-;MVVxZl;Gs>aGbX^^?C@K#ap^v^d+_vh|258GCJS8q00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##An=zI zU}xlW4QKR^nY+)9i7w?mQORUmGJ%#H zZy_p~1T05XGX65!vSVRlkaJ#kOsq!4}xPN2RZxf z1uV`%&OUnqu>*h?G0qM-m!J;;2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0)Li( z;OK?9M)SSddG=)Pz8F!-nEWT$)X~_RMyE4~e-kf2#GJ*O=|haNcN5^8K_3DTfB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz i00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOL}XC-8q8p0pkS literal 0 HcmV?d00001 diff --git a/testutil/datagen/datagen.go b/testutil/datagen/datagen.go new file mode 100644 index 0000000..d38e45d --- /dev/null +++ b/testutil/datagen/datagen.go @@ -0,0 +1,18 @@ +package datagen + +import ( + "math/rand" + + "github.com/babylonlabs-io/babylon/testutil/datagen" + "github.com/babylonlabs-io/vigilante/types" +) + +func GenerateRandomCheckpointRecord(r *rand.Rand) *types.CheckpointRecord { + rawCheckpoint := datagen.GenRandomRawCheckpoint(r) + btcHeight := datagen.RandomIntOtherThan(r, 0, 1000) + + return &types.CheckpointRecord{ + RawCheckpoint: rawCheckpoint, + FirstSeenBtcHeight: uint32(btcHeight), + } +} diff --git a/testutil/datagen/reporter.go b/testutil/datagen/reporter.go new file mode 100644 index 0000000..f627c05 --- /dev/null +++ b/testutil/datagen/reporter.go @@ -0,0 +1,271 @@ +package datagen + +import ( + "math" + "math/big" + "math/rand" + + "github.com/babylonlabs-io/babylon/btctxformatter" + "github.com/babylonlabs-io/babylon/testutil/datagen" + babylontypes "github.com/babylonlabs-io/babylon/types" + "github.com/babylonlabs-io/vigilante/types" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +// calcMerkleRoot creates a merkle tree from the slice of transactions and +// returns the root of the tree. +// (taken from https://github.com/btcsuite/btcd/blob/master/blockchain/fullblocktests/generate.go) +func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash { + if len(txns) == 0 { + return chainhash.Hash{} + } + + utilTxns := make([]*btcutil.Tx, 0, len(txns)) + for _, tx := range txns { + utilTxns = append(utilTxns, btcutil.NewTx(tx)) + } + merkles := blockchain.BuildMerkleTreeStore(utilTxns, false) + return *merkles[len(merkles)-1] +} + +func GetRandomRawBtcCheckpoint(r *rand.Rand) *btctxformatter.RawBtcCheckpoint { + return &btctxformatter.RawBtcCheckpoint{ + Epoch: r.Uint64(), + BlockHash: datagen.GenRandomByteArray(r, btctxformatter.BlockHashLength), + BitMap: datagen.GenRandomByteArray(r, btctxformatter.BitMapLength), + SubmitterAddress: datagen.GenRandomByteArray(r, btctxformatter.AddressLength), + BlsSig: datagen.GenRandomByteArray(r, btctxformatter.BlsSigLength), + } +} + +func GenRandomTx(r *rand.Rand) *wire.MsgTx { + // structure of the below tx is from https://github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go + tx := &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: chainhash.HashH(datagen.GenRandomByteArray(r, 10)), + Index: r.Uint32(), + }, + SignatureScript: datagen.GenRandomByteArray(r, 10), + Sequence: r.Uint32(), + }, + }, + TxOut: []*wire.TxOut{ + { + Value: r.Int63(), + PkScript: datagen.GenRandomByteArray(r, 80), + }, + }, + LockTime: 0, + } + + return tx +} + +func GenRandomBabylonTxPair(r *rand.Rand) ([]*wire.MsgTx, *btctxformatter.RawBtcCheckpoint) { + txs := []*wire.MsgTx{GenRandomTx(r), GenRandomTx(r)} + builder := txscript.NewScriptBuilder() + + // fake a raw checkpoint + rawBTCCkpt := GetRandomRawBtcCheckpoint(r) + // encode raw checkpoint to two halves + firstHalf, secondHalf, err := btctxformatter.EncodeCheckpointData( + []byte{1, 2, 3, 4}, + btctxformatter.CurrentVersion, + rawBTCCkpt, + ) + if err != nil { + panic(err) + } + + dataScript1, err := builder.AddOp(txscript.OP_RETURN).AddData(firstHalf).Script() + if err != nil { + panic(err) + } + txs[0].TxOut[0] = wire.NewTxOut(0, dataScript1) + + // reset builder + builder = txscript.NewScriptBuilder() + + dataScript2, err := builder.AddOp(txscript.OP_RETURN).AddData(secondHalf).Script() + if err != nil { + panic(err) + } + txs[1].TxOut[0] = wire.NewTxOut(0, dataScript2) + + return txs, rawBTCCkpt +} + +func GenRandomBabylonTx(r *rand.Rand) *wire.MsgTx { + tx := GenRandomTx(r) + builder := txscript.NewScriptBuilder() + + // fake a raw checkpoint + rawBTCCkpt := GetRandomRawBtcCheckpoint(r) + // encode raw checkpoint to two halves + firstHalf, secondHalf, err := btctxformatter.EncodeCheckpointData( + []byte{1, 2, 3, 4}, + btctxformatter.CurrentVersion, + rawBTCCkpt, + ) + if err != nil { + panic(err) + } + idx := r.Intn(2) + if idx == 0 { + dataScript, err := builder.AddOp(txscript.OP_RETURN).AddData(firstHalf).Script() + if err != nil { + panic(err) + } + tx.TxOut[0] = wire.NewTxOut(0, dataScript) + } else { + dataScript, err := builder.AddOp(txscript.OP_RETURN).AddData(secondHalf).Script() + if err != nil { + panic(err) + } + tx.TxOut[0] = wire.NewTxOut(0, dataScript) + } + + return tx +} + +func GenRandomBlock(r *rand.Rand, numBabylonTxs int, prevHash *chainhash.Hash) (*wire.MsgBlock, *btctxformatter.RawBtcCheckpoint) { + // create a tx, which will be a Babylon tx with probability `percentage` + var ( + randomTxs []*wire.MsgTx + rawCkpt *btctxformatter.RawBtcCheckpoint + ) + + switch numBabylonTxs { + case 1: + randomTxs, _ = GenRandomBabylonTxPair(r) + randomTxs[1] = GenRandomTx(r) + rawCkpt = nil + case 2: + randomTxs, rawCkpt = GenRandomBabylonTxPair(r) + default: + randomTxs = []*wire.MsgTx{GenRandomTx(r), GenRandomTx(r)} + rawCkpt = nil + } + + coinbaseTx := GenRandomTx(r) + msgTxs := []*wire.MsgTx{coinbaseTx} + msgTxs = append(msgTxs, randomTxs...) + + // calculate correct Merkle root + merkleRoot := calcMerkleRoot(msgTxs) + // don't apply any difficulty + difficulty, _ := new(big.Int).SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16) + workBits := blockchain.BigToCompact(difficulty) + + // find a header that satisfies difficulty + var header *wire.BlockHeader + for { + header = datagen.GenRandomBtcdHeader(r) + header.MerkleRoot = merkleRoot + header.Bits = workBits + + if prevHash == nil { + header.PrevBlock = chainhash.DoubleHashH(datagen.GenRandomByteArray(r, 10)) + } else { + header.PrevBlock = *prevHash + } + + if err := babylontypes.ValidateBTCHeader(header, chaincfg.SimNetParams.PowLimit); err == nil { + break + } + } + + block := &wire.MsgBlock{ + Header: *header, + Transactions: msgTxs, + } + return block, rawCkpt +} + +// GetRandomIndexedBlocks generates a random number of indexed blocks with a random root height +func GetRandomIndexedBlocks(r *rand.Rand, numBlocks uint64) []*types.IndexedBlock { + var ibs []*types.IndexedBlock + + if numBlocks == 0 { + return ibs + } + + block, _ := GenRandomBlock(r, 1, nil) + prevHeight := r.Int31n(math.MaxInt32 - int32(numBlocks)) + ib := types.NewIndexedBlockFromMsgBlock(uint32(prevHeight), block) + prevHash := ib.Header.BlockHash() + + ibs = GetRandomIndexedBlocksFromHeight(r, numBlocks-1, prevHeight, prevHash) + ibs = append([]*types.IndexedBlock{ib}, ibs...) + return ibs +} + +// GetRandomIndexedBlocksFromHeight generates a random number of indexed blocks with a given root height and root hash +func GetRandomIndexedBlocksFromHeight(r *rand.Rand, numBlocks uint64, rootHeight int32, rootHash chainhash.Hash) []*types.IndexedBlock { + var ( + ibs []*types.IndexedBlock + prevHash = rootHash + prevHeight = uint32(rootHeight) + ) + + for i := 0; i < int(numBlocks); i++ { + block, _ := GenRandomBlock(r, 1, &prevHash) + newIb := types.NewIndexedBlockFromMsgBlock(prevHeight+1, block) + ibs = append(ibs, newIb) + + prevHeight = newIb.Height + prevHash = newIb.Header.BlockHash() + } + + return ibs +} + +func GenRandomBlockchainWithBabylonTx(r *rand.Rand, n uint64, partialPercentage float32, fullPercentage float32) ([]*wire.MsgBlock, int, []*btctxformatter.RawBtcCheckpoint) { + blocks := []*wire.MsgBlock{} + numCkptSegs := 0 + rawCkpts := []*btctxformatter.RawBtcCheckpoint{} + // percentage should be [0, 1] + if partialPercentage < 0 || partialPercentage > 1 { + return blocks, 0, rawCkpts + } + if fullPercentage < 0 || fullPercentage > 1 { + return blocks, 0, rawCkpts + } + // n should be > 0 + if n == 0 { + return blocks, 0, rawCkpts + } + + // genesis block + genesisBlock, rawCkpt := GenRandomBlock(r, 0, nil) + blocks = append(blocks, genesisBlock) + rawCkpts = append(rawCkpts, rawCkpt) + + // blocks after genesis + for i := uint64(1); i < n; i++ { + var msgBlock *wire.MsgBlock + prevHash := blocks[len(blocks)-1].BlockHash() + switch { + case r.Float32() < partialPercentage: + msgBlock, rawCkpt = GenRandomBlock(r, 1, &prevHash) + numCkptSegs++ + case r.Float32() < partialPercentage+fullPercentage: + msgBlock, rawCkpt = GenRandomBlock(r, 2, &prevHash) + numCkptSegs += 2 + default: + msgBlock, rawCkpt = GenRandomBlock(r, 0, &prevHash) + } + + blocks = append(blocks, msgBlock) + rawCkpts = append(rawCkpts, rawCkpt) + } + return blocks, numCkptSegs, rawCkpts +} diff --git a/testutil/mocks/btcclient.go b/testutil/mocks/btcclient.go new file mode 100644 index 0000000..008c3a5 --- /dev/null +++ b/testutil/mocks/btcclient.go @@ -0,0 +1,448 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: btcclient/interface.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + btcclient "github.com/babylonlabs-io/vigilante/btcclient" + config "github.com/babylonlabs-io/vigilante/config" + types "github.com/babylonlabs-io/vigilante/types" + btcjson "github.com/btcsuite/btcd/btcjson" + btcutil "github.com/btcsuite/btcd/btcutil" + chaincfg "github.com/btcsuite/btcd/chaincfg" + chainhash "github.com/btcsuite/btcd/chaincfg/chainhash" + wire "github.com/btcsuite/btcd/wire" + gomock "github.com/golang/mock/gomock" + chainntnfs "github.com/lightningnetwork/lnd/chainntnfs" +) + +// MockBTCClient is a mock of BTCClient interface. +type MockBTCClient struct { + ctrl *gomock.Controller + recorder *MockBTCClientMockRecorder +} + +// MockBTCClientMockRecorder is the mock recorder for MockBTCClient. +type MockBTCClientMockRecorder struct { + mock *MockBTCClient +} + +// NewMockBTCClient creates a new mock instance. +func NewMockBTCClient(ctrl *gomock.Controller) *MockBTCClient { + mock := &MockBTCClient{ctrl: ctrl} + mock.recorder = &MockBTCClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBTCClient) EXPECT() *MockBTCClientMockRecorder { + return m.recorder +} + +// FindTailBlocksByHeight mocks base method. +func (m *MockBTCClient) FindTailBlocksByHeight(height uint32) ([]*types.IndexedBlock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindTailBlocksByHeight", height) + ret0, _ := ret[0].([]*types.IndexedBlock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindTailBlocksByHeight indicates an expected call of FindTailBlocksByHeight. +func (mr *MockBTCClientMockRecorder) FindTailBlocksByHeight(height interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindTailBlocksByHeight", reflect.TypeOf((*MockBTCClient)(nil).FindTailBlocksByHeight), height) +} + +// GetBestBlock mocks base method. +func (m *MockBTCClient) GetBestBlock() (uint32, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBestBlock") + ret0, _ := ret[0].(uint32) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBestBlock indicates an expected call of GetBestBlock. +func (mr *MockBTCClientMockRecorder) GetBestBlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBestBlock", reflect.TypeOf((*MockBTCClient)(nil).GetBestBlock)) +} + +// GetBlockByHash mocks base method. +func (m *MockBTCClient) GetBlockByHash(blockHash *chainhash.Hash) (*types.IndexedBlock, *wire.MsgBlock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlockByHash", blockHash) + ret0, _ := ret[0].(*types.IndexedBlock) + ret1, _ := ret[1].(*wire.MsgBlock) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetBlockByHash indicates an expected call of GetBlockByHash. +func (mr *MockBTCClientMockRecorder) GetBlockByHash(blockHash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockByHash", reflect.TypeOf((*MockBTCClient)(nil).GetBlockByHash), blockHash) +} + +// GetBlockByHeight mocks base method. +func (m *MockBTCClient) GetBlockByHeight(height uint32) (*types.IndexedBlock, *wire.MsgBlock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlockByHeight", height) + ret0, _ := ret[0].(*types.IndexedBlock) + ret1, _ := ret[1].(*wire.MsgBlock) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetBlockByHeight indicates an expected call of GetBlockByHeight. +func (mr *MockBTCClientMockRecorder) GetBlockByHeight(height interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockByHeight", reflect.TypeOf((*MockBTCClient)(nil).GetBlockByHeight), height) +} + +// GetRawTransaction mocks base method. +func (m *MockBTCClient) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRawTransaction", txHash) + ret0, _ := ret[0].(*btcutil.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRawTransaction indicates an expected call of GetRawTransaction. +func (mr *MockBTCClientMockRecorder) GetRawTransaction(txHash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRawTransaction", reflect.TypeOf((*MockBTCClient)(nil).GetRawTransaction), txHash) +} + +// GetTransaction mocks base method. +func (m *MockBTCClient) GetTransaction(txHash *chainhash.Hash) (*btcjson.GetTransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTransaction", txHash) + ret0, _ := ret[0].(*btcjson.GetTransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTransaction indicates an expected call of GetTransaction. +func (mr *MockBTCClientMockRecorder) GetTransaction(txHash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransaction", reflect.TypeOf((*MockBTCClient)(nil).GetTransaction), txHash) +} + +// GetTxOut mocks base method. +func (m *MockBTCClient) GetTxOut(txHash *chainhash.Hash, index uint32, mempool bool) (*btcjson.GetTxOutResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTxOut", txHash, index, mempool) + ret0, _ := ret[0].(*btcjson.GetTxOutResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTxOut indicates an expected call of GetTxOut. +func (mr *MockBTCClientMockRecorder) GetTxOut(txHash, index, mempool interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTxOut", reflect.TypeOf((*MockBTCClient)(nil).GetTxOut), txHash, index, mempool) +} + +// SendRawTransaction mocks base method. +func (m *MockBTCClient) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendRawTransaction", tx, allowHighFees) + ret0, _ := ret[0].(*chainhash.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendRawTransaction indicates an expected call of SendRawTransaction. +func (mr *MockBTCClientMockRecorder) SendRawTransaction(tx, allowHighFees interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRawTransaction", reflect.TypeOf((*MockBTCClient)(nil).SendRawTransaction), tx, allowHighFees) +} + +// Stop mocks base method. +func (m *MockBTCClient) Stop() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Stop") +} + +// Stop indicates an expected call of Stop. +func (mr *MockBTCClientMockRecorder) Stop() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockBTCClient)(nil).Stop)) +} + +// TxDetails mocks base method. +func (m *MockBTCClient) TxDetails(txHash *chainhash.Hash, pkScript []byte) (*chainntnfs.TxConfirmation, btcclient.TxStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TxDetails", txHash, pkScript) + ret0, _ := ret[0].(*chainntnfs.TxConfirmation) + ret1, _ := ret[1].(btcclient.TxStatus) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// TxDetails indicates an expected call of TxDetails. +func (mr *MockBTCClientMockRecorder) TxDetails(txHash, pkScript interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TxDetails", reflect.TypeOf((*MockBTCClient)(nil).TxDetails), txHash, pkScript) +} + +// WaitForShutdown mocks base method. +func (m *MockBTCClient) WaitForShutdown() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "WaitForShutdown") +} + +// WaitForShutdown indicates an expected call of WaitForShutdown. +func (mr *MockBTCClientMockRecorder) WaitForShutdown() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForShutdown", reflect.TypeOf((*MockBTCClient)(nil).WaitForShutdown)) +} + +// MockBTCWallet is a mock of BTCWallet interface. +type MockBTCWallet struct { + ctrl *gomock.Controller + recorder *MockBTCWalletMockRecorder +} + +// MockBTCWalletMockRecorder is the mock recorder for MockBTCWallet. +type MockBTCWalletMockRecorder struct { + mock *MockBTCWallet +} + +// NewMockBTCWallet creates a new mock instance. +func NewMockBTCWallet(ctrl *gomock.Controller) *MockBTCWallet { + mock := &MockBTCWallet{ctrl: ctrl} + mock.recorder = &MockBTCWalletMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBTCWallet) EXPECT() *MockBTCWalletMockRecorder { + return m.recorder +} + +// FundRawTransaction mocks base method. +func (m *MockBTCWallet) FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FundRawTransaction", tx, opts, isWitness) + ret0, _ := ret[0].(*btcjson.FundRawTransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FundRawTransaction indicates an expected call of FundRawTransaction. +func (mr *MockBTCWalletMockRecorder) FundRawTransaction(tx, opts, isWitness interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FundRawTransaction", reflect.TypeOf((*MockBTCWallet)(nil).FundRawTransaction), tx, opts, isWitness) +} + +// GetBTCConfig mocks base method. +func (m *MockBTCWallet) GetBTCConfig() *config.BTCConfig { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBTCConfig") + ret0, _ := ret[0].(*config.BTCConfig) + return ret0 +} + +// GetBTCConfig indicates an expected call of GetBTCConfig. +func (mr *MockBTCWalletMockRecorder) GetBTCConfig() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBTCConfig", reflect.TypeOf((*MockBTCWallet)(nil).GetBTCConfig)) +} + +// GetHighUTXOAndSum mocks base method. +func (m *MockBTCWallet) GetHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHighUTXOAndSum") + ret0, _ := ret[0].(*btcjson.ListUnspentResult) + ret1, _ := ret[1].(float64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetHighUTXOAndSum indicates an expected call of GetHighUTXOAndSum. +func (mr *MockBTCWalletMockRecorder) GetHighUTXOAndSum() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHighUTXOAndSum", reflect.TypeOf((*MockBTCWallet)(nil).GetHighUTXOAndSum)) +} + +// GetNetParams mocks base method. +func (m *MockBTCWallet) GetNetParams() *chaincfg.Params { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetParams") + ret0, _ := ret[0].(*chaincfg.Params) + return ret0 +} + +// GetNetParams indicates an expected call of GetNetParams. +func (mr *MockBTCWalletMockRecorder) GetNetParams() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetParams", reflect.TypeOf((*MockBTCWallet)(nil).GetNetParams)) +} + +// GetRawChangeAddress mocks base method. +func (m *MockBTCWallet) GetRawChangeAddress(account string) (btcutil.Address, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRawChangeAddress", account) + ret0, _ := ret[0].(btcutil.Address) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRawChangeAddress indicates an expected call of GetRawChangeAddress. +func (mr *MockBTCWalletMockRecorder) GetRawChangeAddress(account interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRawChangeAddress", reflect.TypeOf((*MockBTCWallet)(nil).GetRawChangeAddress), account) +} + +// GetRawTransaction mocks base method. +func (m *MockBTCWallet) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRawTransaction", txHash) + ret0, _ := ret[0].(*btcutil.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRawTransaction indicates an expected call of GetRawTransaction. +func (mr *MockBTCWalletMockRecorder) GetRawTransaction(txHash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRawTransaction", reflect.TypeOf((*MockBTCWallet)(nil).GetRawTransaction), txHash) +} + +// GetWalletLockTime mocks base method. +func (m *MockBTCWallet) GetWalletLockTime() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetWalletLockTime") + ret0, _ := ret[0].(int64) + return ret0 +} + +// GetWalletLockTime indicates an expected call of GetWalletLockTime. +func (mr *MockBTCWalletMockRecorder) GetWalletLockTime() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWalletLockTime", reflect.TypeOf((*MockBTCWallet)(nil).GetWalletLockTime)) +} + +// GetWalletPass mocks base method. +func (m *MockBTCWallet) GetWalletPass() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetWalletPass") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetWalletPass indicates an expected call of GetWalletPass. +func (mr *MockBTCWalletMockRecorder) GetWalletPass() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWalletPass", reflect.TypeOf((*MockBTCWallet)(nil).GetWalletPass)) +} + +// ListReceivedByAddress mocks base method. +func (m *MockBTCWallet) ListReceivedByAddress() ([]btcjson.ListReceivedByAddressResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListReceivedByAddress") + ret0, _ := ret[0].([]btcjson.ListReceivedByAddressResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListReceivedByAddress indicates an expected call of ListReceivedByAddress. +func (mr *MockBTCWalletMockRecorder) ListReceivedByAddress() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListReceivedByAddress", reflect.TypeOf((*MockBTCWallet)(nil).ListReceivedByAddress)) +} + +// ListUnspent mocks base method. +func (m *MockBTCWallet) ListUnspent() ([]btcjson.ListUnspentResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListUnspent") + ret0, _ := ret[0].([]btcjson.ListUnspentResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListUnspent indicates an expected call of ListUnspent. +func (mr *MockBTCWalletMockRecorder) ListUnspent() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUnspent", reflect.TypeOf((*MockBTCWallet)(nil).ListUnspent)) +} + +// SendRawTransaction mocks base method. +func (m *MockBTCWallet) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendRawTransaction", tx, allowHighFees) + ret0, _ := ret[0].(*chainhash.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendRawTransaction indicates an expected call of SendRawTransaction. +func (mr *MockBTCWalletMockRecorder) SendRawTransaction(tx, allowHighFees interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRawTransaction", reflect.TypeOf((*MockBTCWallet)(nil).SendRawTransaction), tx, allowHighFees) +} + +// SignRawTransactionWithWallet mocks base method. +func (m *MockBTCWallet) SignRawTransactionWithWallet(tx *wire.MsgTx) (*wire.MsgTx, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignRawTransactionWithWallet", tx) + ret0, _ := ret[0].(*wire.MsgTx) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// SignRawTransactionWithWallet indicates an expected call of SignRawTransactionWithWallet. +func (mr *MockBTCWalletMockRecorder) SignRawTransactionWithWallet(tx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignRawTransactionWithWallet", reflect.TypeOf((*MockBTCWallet)(nil).SignRawTransactionWithWallet), tx) +} + +// Stop mocks base method. +func (m *MockBTCWallet) Stop() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Stop") +} + +// Stop indicates an expected call of Stop. +func (mr *MockBTCWalletMockRecorder) Stop() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockBTCWallet)(nil).Stop)) +} + +// TxDetails mocks base method. +func (m *MockBTCWallet) TxDetails(txHash *chainhash.Hash, pkScript []byte) (*chainntnfs.TxConfirmation, btcclient.TxStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TxDetails", txHash, pkScript) + ret0, _ := ret[0].(*chainntnfs.TxConfirmation) + ret1, _ := ret[1].(btcclient.TxStatus) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// TxDetails indicates an expected call of TxDetails. +func (mr *MockBTCWalletMockRecorder) TxDetails(txHash, pkScript interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TxDetails", reflect.TypeOf((*MockBTCWallet)(nil).TxDetails), txHash, pkScript) +} + +// WalletPassphrase mocks base method. +func (m *MockBTCWallet) WalletPassphrase(passphrase string, timeoutSecs int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WalletPassphrase", passphrase, timeoutSecs) + ret0, _ := ret[0].(error) + return ret0 +} + +// WalletPassphrase indicates an expected call of WalletPassphrase. +func (mr *MockBTCWalletMockRecorder) WalletPassphrase(passphrase, timeoutSecs interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WalletPassphrase", reflect.TypeOf((*MockBTCWallet)(nil).WalletPassphrase), passphrase, timeoutSecs) +} diff --git a/testutil/port.go b/testutil/port.go new file mode 100644 index 0000000..19ad7f7 --- /dev/null +++ b/testutil/port.go @@ -0,0 +1,61 @@ +package testutil + +import ( + "fmt" + mrand "math/rand/v2" + "net" + "sync" + "testing" +) + +// Track allocated ports, protected by a mutex +var ( + allocatedPorts = make(map[int]struct{}) + portMutex sync.Mutex +) + +// AllocateUniquePort tries to find an available TCP port on the localhost +// by testing multiple random ports within a specified range. +func AllocateUniquePort(t *testing.T) int { + randPort := func(base, spread int) int { + return base + mrand.IntN(spread) + } + + // Base port and spread range for port selection + const ( + basePort = 20000 + portRange = 30000 + ) + + // Try up to 10 times to find an available port + for i := 0; i < 10; i++ { + port := randPort(basePort, portRange) + + // Lock the mutex to check and modify the shared map + portMutex.Lock() + if _, exists := allocatedPorts[port]; exists { + // Port already allocated, try another one + portMutex.Unlock() + continue + } + + listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + portMutex.Unlock() + continue + } + + allocatedPorts[port] = struct{}{} + portMutex.Unlock() + + if err := listener.Close(); err != nil { + continue + } + + return port + } + + // If no available port was found, fail the test + t.Fatalf("failed to find an available port in range %d-%d", basePort, basePort+portRange) + return 0 +} diff --git a/testutil/store.go b/testutil/store.go new file mode 100644 index 0000000..63d66dc --- /dev/null +++ b/testutil/store.go @@ -0,0 +1,26 @@ +package testutil + +import ( + "github.com/babylonlabs-io/vigilante/config" + "github.com/lightningnetwork/lnd/kvdb" + "github.com/stretchr/testify/require" + "testing" +) + +func MakeTestBackend(t *testing.T) kvdb.Backend { + tempDirName := t.TempDir() + + cfg := config.DefaultDBConfig() + + cfg.DBPath = tempDirName + + backend, err := cfg.GetDBBackend() + require.NoError(t, err) + + t.Cleanup(func() { + err := backend.Close() + require.NoError(t, err) + }) + + return backend +} diff --git a/testutil/version.go b/testutil/version.go new file mode 100644 index 0000000..5a2dfaf --- /dev/null +++ b/testutil/version.go @@ -0,0 +1,32 @@ +package testutil + +import ( + "fmt" + "golang.org/x/mod/modfile" + "os" + "path/filepath" +) + +// GetBabylonVersion returns babylond version from go.mod +func GetBabylonVersion() (string, error) { + goModPath := filepath.Join("..", "go.mod") + data, err := os.ReadFile(goModPath) + if err != nil { + return "", err + } + + // Parse the go.mod file + modFile, err := modfile.Parse("go.mod", data, nil) + if err != nil { + return "", err + } + + const modName = "github.com/babylonlabs-io/babylon" + for _, require := range modFile.Require { + if require.Mod.Path == modName { + return require.Mod.Version, nil + } + } + + return "", fmt.Errorf("module %s not found", modName) +} From 27cca29e95db5153220bb55999fda0ca841be1a2 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 13:55:36 +0530 Subject: [PATCH 04/32] fix imports --- e2etest/bitcoind_node_setup.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/e2etest/bitcoind_node_setup.go b/e2etest/bitcoind_node_setup.go index 7a7a096..b6b6380 100644 --- a/e2etest/bitcoind_node_setup.go +++ b/e2etest/bitcoind_node_setup.go @@ -3,14 +3,15 @@ package e2etest import ( "encoding/json" "fmt" - "github.com/babylonlabs-io/vigilante/e2etest/container" - "github.com/ory/dockertest/v3" - "github.com/stretchr/testify/require" "os" "strconv" "strings" "testing" "time" + + "github.com/babylonlabs-io/babylon-staking-indexer/e2etest/container" + "github.com/ory/dockertest/v3" + "github.com/stretchr/testify/require" ) var ( From 0e7c0cbfda638951438233bcbf89c017001b1406 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 16:21:09 +0530 Subject: [PATCH 05/32] fix --- e2etest/atomicslasher_e2e_test.go | 290 --------- e2etest/log.go | 10 - e2etest/monitor_e2e_test.go | 150 ----- e2etest/reporter_e2e_test.go | 243 -------- e2etest/slasher_e2e_test.go | 290 --------- e2etest/submitter_e2e_test.go | 195 ------ e2etest/test_manager.go | 224 ++----- e2etest/test_manager_btcstaking.go | 898 --------------------------- e2etest/unbondingwatcher_e2e_test.go | 336 ---------- internal/config/btc.go | 29 + internal/config/config.go | 21 + internal/config/db.go | 17 + 12 files changed, 116 insertions(+), 2587 deletions(-) delete mode 100644 e2etest/atomicslasher_e2e_test.go delete mode 100644 e2etest/log.go delete mode 100644 e2etest/monitor_e2e_test.go delete mode 100644 e2etest/reporter_e2e_test.go delete mode 100644 e2etest/slasher_e2e_test.go delete mode 100644 e2etest/submitter_e2e_test.go delete mode 100644 e2etest/test_manager_btcstaking.go delete mode 100644 e2etest/unbondingwatcher_e2e_test.go diff --git a/e2etest/atomicslasher_e2e_test.go b/e2etest/atomicslasher_e2e_test.go deleted file mode 100644 index 8ada2f9..0000000 --- a/e2etest/atomicslasher_e2e_test.go +++ /dev/null @@ -1,290 +0,0 @@ -//go:build e2e -// +build e2e - -package e2etest - -import ( - "go.uber.org/zap" - "testing" - "time" - - bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - "github.com/babylonlabs-io/vigilante/btcclient" - bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" - "github.com/babylonlabs-io/vigilante/btcstaking-tracker/btcslasher" - "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/metrics" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/stretchr/testify/require" -) - -// TestAtomicSlasher verifies the behavior of the atomic slasher by setting up delegations, -// sending slashing transactions, and ensuring that slashing is detected and executed correctly. -func TestAtomicSlasher(t *testing.T) { - t.Parallel() - // segwit is activated at height 300. It's needed by staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - - // start WebSocket connection with Babylon for subscriber services - err := tm.BabylonClient.Start() - require.NoError(t, err) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &btcclient.EmptyHintCache{}, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - backend, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - metrics.NewBTCStakingTrackerMetrics(), - ) - go bsTracker.Start() - defer bsTracker.Stop() - - bsParamsResp, err := tm.BabylonClient.BTCStakingParams() - require.NoError(t, err) - bsParams := bsParamsResp.Params - - // set up a finality provider - btcFP, fpSK := tm.CreateFinalityProvider(t) - // set up 2 BTC delegations - tm.CreateBTCDelegation(t, fpSK) - tm.CreateBTCDelegation(t, fpSK) - - // retrieve 2 BTC delegations - btcDelsResp, err := tm.BabylonClient.BTCDelegations(bstypes.BTCDelegationStatus_ACTIVE, nil) - require.NoError(t, err) - require.Len(t, btcDelsResp.BtcDelegations, 2) - btcDels := btcDelsResp.BtcDelegations - - /* - finality provider builds slashing tx witness and sends slashing tx to Bitcoin - */ - victimBTCDel := btcDels[0] - victimSlashingTx, err := btcslasher.BuildSlashingTxWithWitness(victimBTCDel, &bsParams, regtestParams, fpSK) - // send slashing tx to Bitcoin - require.NoError(t, err) - slashingTxHash, err := tm.BTCClient.SendRawTransaction(victimSlashingTx, true) - require.NoError(t, err) - - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(slashingTxHash) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // mine a block that includes slashing tx, which will trigger atomic slasher - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - minedBlock := tm.mineBlock(t) - require.Equal(t, 2, len(minedBlock.Transactions)) - - /* - atomic slasher will detect the selective slashing on victim BTC delegation - the finality provider will get slashed on Babylon - */ - require.Eventually(t, func() bool { - resp, err := tm.BabylonClient.FinalityProvider(btcFP.BtcPk.MarshalHex()) - if err != nil { - return false - } - return resp.FinalityProvider.SlashedBabylonHeight > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - /* - atomic slasher will slash the other BTC delegation on Bitcoin - */ - btcDel2 := btcDels[1] - slashTx2, err := bstypes.NewBTCSlashingTxFromHex(btcDel2.SlashingTxHex) - require.NoError(t, err) - slashingTxHash2 := slashTx2.MustGetTxHash() - - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(slashingTxHash2) - if err != nil { - t.Logf("err of getting slashingTxHash of the BTC delegation affected by atomic slashing: %v", err) - } - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // mine a block that includes slashing tx, which will trigger atomic slasher - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash2})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - minedBlock = tm.mineBlock(t) - require.Equal(t, 2, len(minedBlock.Transactions)) -} - -// TestAtomicSlasher_Unbonding tests the atomic slasher's handling of unbonding BTC delegations, -// including the creation and detection of unbonding slashing transactions. -func TestAtomicSlasher_Unbonding(t *testing.T) { - t.Parallel() - // segwit is activated at height 300. It's needed by staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - - // start WebSocket connection with Babylon for subscriber services - err := tm.BabylonClient.Start() - require.NoError(t, err) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - emptyHintCache := btcclient.EmptyHintCache{} - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &emptyHintCache, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - backend, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - go bsTracker.Start() - defer bsTracker.Stop() - - bsParamsResp, err := tm.BabylonClient.BTCStakingParams() - require.NoError(t, err) - bsParams := bsParamsResp.Params - - // set up a finality provider - btcFP, fpSK := tm.CreateFinalityProvider(t) - - // set up 1st BTC delegation, which will be later used as the victim - stakingSlashingInfo, unbondingSlashingInfo, btcDelSK := tm.CreateBTCDelegation(t, fpSK) - btcDelsResp, err := tm.BabylonClient.BTCDelegations(bstypes.BTCDelegationStatus_ACTIVE, nil) - require.NoError(t, err) - require.Len(t, btcDelsResp.BtcDelegations, 1) - victimBTCDel := btcDelsResp.BtcDelegations[0] - - // set up 2nd BTC delegation, which will be subjected to atomic slashing - tm.CreateBTCDelegation(t, fpSK) - btcDelsResp2, err := tm.BabylonClient.BTCDelegations(bstypes.BTCDelegationStatus_ACTIVE, nil) - require.NoError(t, err) - require.Len(t, btcDelsResp2.BtcDelegations, 2) - - // NOTE: `BTCDelegations` API does not return BTC delegations in created time order - // thus we need to find out the 2nd BTC delegation one-by-one - var btcDel2 *bstypes.BTCDelegationResponse - for _, delResp2 := range btcDelsResp2.BtcDelegations { - if delResp2.StakingTxHex != victimBTCDel.StakingTxHex { - btcDel2 = delResp2 - break - } - } - - require.NotNil(t, btcDel2, "err second delegation not found") - - /* - the victim BTC delegation unbonds - */ - tm.Undelegate(t, stakingSlashingInfo, unbondingSlashingInfo, btcDelSK, func() { tm.CatchUpBTCLightClient(t) }) - - /* - finality provider builds unbonding slashing tx witness and sends it to Bitcoin - */ - victimUnbondingSlashingTx, err := btcslasher.BuildUnbondingSlashingTxWithWitness(victimBTCDel, &bsParams, regtestParams, fpSK) - require.NoError(t, err) - - // send slashing tx to Bitcoin - // NOTE: sometimes unbonding slashing tx is not immediately spendable for some reason - var unbondingSlashingTxHash *chainhash.Hash - require.Eventually(t, func() bool { - unbondingSlashingTxHash, err = tm.BTCClient.SendRawTransaction(victimUnbondingSlashingTx, true) - if err != nil { - t.Logf("err of SendRawTransaction: %v", err) - return false - } - return true - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // unbonding slashing tx is eventually queryable - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(unbondingSlashingTxHash) - if err != nil { - t.Logf("err of GetRawTransaction: %v", err) - return false - } - return true - }, eventuallyWaitTimeOut, eventuallyPollTime) - // mine a block that includes unbonding slashing tx, which will trigger atomic slasher - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingSlashingTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - minedBlock := tm.mineBlock(t) - require.Equal(t, 2, len(minedBlock.Transactions)) - - /* - atomic slasher will detect the selective slashing on victim BTC delegation - the finality provider will get slashed on Babylon - */ - require.Eventually(t, func() bool { - resp, err := tm.BabylonClient.FinalityProvider(btcFP.BtcPk.MarshalHex()) - if err != nil { - return false - } - return resp.FinalityProvider.SlashedBabylonHeight > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - /* - atomic slasher will slash the other BTC delegation on Bitcoin - */ - slashingTx2, err := bstypes.NewBTCSlashingTxFromHex(btcDel2.SlashingTxHex) - require.NoError(t, err) - - slashingTxHash2 := slashingTx2.MustGetTxHash() - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(slashingTxHash2) - t.Logf("err of getting slashingTxHash of the BTC delegation affected by atomic slashing: %v", err) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // mine a block that includes slashing tx, which will trigger atomic slasher - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash2})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - minedBlock = tm.mineBlock(t) - require.Equal(t, 2, len(minedBlock.Transactions)) -} diff --git a/e2etest/log.go b/e2etest/log.go deleted file mode 100644 index b7a99f5..0000000 --- a/e2etest/log.go +++ /dev/null @@ -1,10 +0,0 @@ -package e2etest - -import ( - "github.com/babylonlabs-io/vigilante/config" -) - -var ( - logger, _ = config.NewRootLogger("auto", "debug") - log = logger.Sugar() -) diff --git a/e2etest/monitor_e2e_test.go b/e2etest/monitor_e2e_test.go deleted file mode 100644 index 844a89c..0000000 --- a/e2etest/monitor_e2e_test.go +++ /dev/null @@ -1,150 +0,0 @@ -//go:build e2e -// +build e2e - -package e2etest - -import ( - "fmt" - bbnclient "github.com/babylonlabs-io/babylon/client/client" - "github.com/babylonlabs-io/vigilante/btcclient" - "github.com/babylonlabs-io/vigilante/metrics" - "github.com/babylonlabs-io/vigilante/monitor" - "github.com/babylonlabs-io/vigilante/reporter" - "github.com/babylonlabs-io/vigilante/submitter" - "github.com/babylonlabs-io/vigilante/testutil" - "github.com/babylonlabs-io/vigilante/types" - "github.com/btcsuite/btcd/chaincfg" - sdk "github.com/cosmos/cosmos-sdk/types" - promtestutil "github.com/prometheus/client_golang/prometheus/testutil" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - "time" - - "testing" -) - -// TestMonitorBootstrap - validates that after a restart monitor bootstraps from DB -func TestMonitorBootstrap(t *testing.T) { - t.Parallel() - numMatureOutputs := uint32(150) - - tm := StartManager(t, numMatureOutputs, 2) - defer tm.Stop(t) - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &btcclient.EmptyHintCache{}, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - dbBackend := testutil.MakeTestBackend(t) - - monitorMetrics := metrics.NewMonitorMetrics() - genesisPath := fmt.Sprintf("%s/config/genesis.json", tm.Config.Babylon.KeyDirectory) - genesisInfo, err := types.GetGenesisInfoFromFile(genesisPath) - require.NoError(t, err) - - tm.Config.Submitter.PollingIntervalSeconds = 1 - subAddr, _ := sdk.AccAddressFromBech32(submitterAddrStr) - - // create submitter - vigilantSubmitter, _ := submitter.New( - &tm.Config.Submitter, - logger, - tm.BTCClient, - tm.BabylonClient, - subAddr, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - tm.Config.Common.MaxRetryTimes, - metrics.NewSubmitterMetrics(), - testutil.MakeTestBackend(t), - tm.Config.BTC.WalletName, - ) - - vigilantSubmitter.Start() - defer vigilantSubmitter.Stop() - - vigilantReporter, err := reporter.New( - &tm.Config.Reporter, - logger, - tm.BTCClient, - tm.BabylonClient, - backend, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - metrics.NewReporterMetrics(), - ) - require.NoError(t, err) - - defer func() { - vigilantSubmitter.Stop() - vigilantSubmitter.WaitForShutdown() - }() - - mon, err := monitor.New( - &tm.Config.Monitor, - &tm.Config.Common, - zap.NewNop(), - genesisInfo, - tm.BabylonClient, - tm.BTCClient, - backend, - monitorMetrics, - dbBackend, - ) - require.NoError(t, err) - vigilantReporter.Start() - defer vigilantReporter.Stop() - - go func() { - ticker := time.NewTicker(3 * time.Second) - timer := time.NewTimer(15 * time.Second) - defer timer.Stop() - defer ticker.Stop() - for { - select { - case <-ticker.C: - tm.mineBlock(t) - case <-timer.C: - return - } - } - }() - - go mon.Start(genesisInfo.GetBaseBTCHeight()) - - time.Sleep(15 * time.Second) - mon.Stop() - - // use a new bbn client - babylonClient, err := bbnclient.New(&tm.Config.Babylon, nil) - require.NoError(t, err) - defer babylonClient.Stop() - - mon, err = monitor.New( - &tm.Config.Monitor, - &tm.Config.Common, - zap.NewNop(), - genesisInfo, - babylonClient, - tm.BTCClient, - backend, - monitorMetrics, - dbBackend, - ) - require.NoError(t, err) - go mon.Start(genesisInfo.GetBaseBTCHeight()) - - defer mon.Stop() - - require.Zero(t, promtestutil.ToFloat64(mon.Metrics().InvalidBTCHeadersCounter)) - require.Zero(t, promtestutil.ToFloat64(mon.Metrics().InvalidEpochsCounter)) - require.Eventually(t, func() bool { - return mon.BTCScanner.GetBaseHeight() > genesisInfo.GetBaseBTCHeight() - }, eventuallyWaitTimeOut, eventuallyPollTime) -} diff --git a/e2etest/reporter_e2e_test.go b/e2etest/reporter_e2e_test.go deleted file mode 100644 index accb120..0000000 --- a/e2etest/reporter_e2e_test.go +++ /dev/null @@ -1,243 +0,0 @@ -//go:build e2e -// +build e2e - -package e2etest - -import ( - "sync" - "testing" - "time" - - "github.com/babylonlabs-io/vigilante/btcclient" - "github.com/babylonlabs-io/vigilante/netparams" - - "github.com/babylonlabs-io/vigilante/metrics" - "github.com/babylonlabs-io/vigilante/reporter" - "github.com/stretchr/testify/require" -) - -var ( - longEventuallyWaitTimeOut = 2 * eventuallyWaitTimeOut -) - -func (tm *TestManager) BabylonBTCChainMatchesBtc(t *testing.T) bool { - tipHeight, err := tm.TestRpcClient.GetBlockCount() - require.NoError(t, err) - tipHash, err := tm.TestRpcClient.GetBlockHash(tipHeight) - require.NoError(t, err) - bbnBtcLcTip, err := tm.BabylonClient.BTCHeaderChainTip() - require.NoError(t, err) - - return uint32(tipHeight) == bbnBtcLcTip.Header.Height && tipHash.String() == bbnBtcLcTip.Header.HashHex -} - -func (tm *TestManager) GenerateAndSubmitBlockNBlockStartingFromDepth(t *testing.T, N int, depth uint32) { - if depth == 0 { - // depth 0 means we are starting from tip - tm.BitcoindHandler.GenerateBlocks(N) - return - } - - height, err := tm.TestRpcClient.GetBlockCount() - require.NoError(t, err) - - startingBlockHeight := height - int64(depth) - - blockHash, err := tm.TestRpcClient.GetBlockHash(startingBlockHeight) - require.NoError(t, err) - - // invalidate blocks from this height - tm.BitcoindHandler.InvalidateBlock(blockHash.String()) - - for i := 0; i < N; i++ { - tm.BitcoindHandler.GenerateBlocks(N) - } -} - -func TestReporter_BoostrapUnderFrequentBTCHeaders(t *testing.T) { - //t.Parallel() // todo(lazar): this test when run in parallel is very flaky, investigate why - // no need to much mature outputs, we are not going to submit transactions in this test - numMatureOutputs := uint32(150) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - - reporterMetrics := metrics.NewReporterMetrics() - - // create the chain notifier - btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) - require.NoError(t, err) - btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) - btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) - require.NoError(t, err) - - vigilantReporter, err := reporter.New( - &tm.Config.Reporter, - logger, - tm.BTCClient, - tm.BabylonClient, - btcNotifier, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - reporterMetrics, - ) - require.NoError(t, err) - - // start a routine that mines BTC blocks very fast - var wg sync.WaitGroup - stopChan := make(chan struct{}) - - wg.Add(1) - go func() { - defer wg.Done() - ticker := time.NewTicker(10 * time.Second) - defer ticker.Stop() - for { - select { - case <-ticker.C: - tm.BitcoindHandler.GenerateBlocks(1) - case <-stopChan: - return - } - } - }() - - // mine some BTC headers - tm.BitcoindHandler.GenerateBlocks(1) - - // start reporter - vigilantReporter.Start() - defer vigilantReporter.Stop() - - // tips should eventually match - require.Eventually(t, func() bool { - return tm.BabylonBTCChainMatchesBtc(t) - }, longEventuallyWaitTimeOut, eventuallyPollTime) - - close(stopChan) - wg.Wait() -} - -func TestRelayHeadersAndHandleRollbacks(t *testing.T) { - t.Parallel() - // no need to much mature outputs, we are not going to submit transactions in this test - numMatureOutputs := uint32(150) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - // this is necessary to receive notifications about new transactions entering mempool - defer tm.Stop(t) - - reporterMetrics := metrics.NewReporterMetrics() - - btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) - require.NoError(t, err) - btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) - btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) - require.NoError(t, err) - - vigilantReporter, err := reporter.New( - &tm.Config.Reporter, - logger, - tm.BTCClient, - tm.BabylonClient, - btcNotifier, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - reporterMetrics, - ) - require.NoError(t, err) - - vigilantReporter.Start() - defer vigilantReporter.Stop() - - require.Eventually(t, func() bool { - return tm.BabylonBTCChainMatchesBtc(t) - }, longEventuallyWaitTimeOut, eventuallyPollTime) - - // generate 3, we are submitting headers 1 by 1 so we use small amount as this is slow process - tm.BitcoindHandler.GenerateBlocks(3) - - require.Eventually(t, func() bool { - return tm.BabylonBTCChainMatchesBtc(t) - }, longEventuallyWaitTimeOut, eventuallyPollTime) - - // we will start from block before tip and submit 2 new block this should trigger rollback - tm.GenerateAndSubmitBlockNBlockStartingFromDepth(t, 2, 1) - - // tips should eventually match - require.Eventually(t, func() bool { - return tm.BabylonBTCChainMatchesBtc(t) - }, longEventuallyWaitTimeOut, eventuallyPollTime) -} - -func TestHandleReorgAfterRestart(t *testing.T) { - t.Parallel() - // no need to much mature outputs, we are not going to submit transactions in this test - numMatureOutputs := uint32(150) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - // this is necessary to receive notifications about new transactions entering mempool - defer tm.Stop(t) - - reporterMetrics := metrics.NewReporterMetrics() - - btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) - require.NoError(t, err) - btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) - btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) - require.NoError(t, err) - - vigilantReporter, err := reporter.New( - &tm.Config.Reporter, - logger, - tm.BTCClient, - tm.BabylonClient, - btcNotifier, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - reporterMetrics, - ) - require.NoError(t, err) - - vigilantReporter.Start() - - require.Eventually(t, func() bool { - return tm.BabylonBTCChainMatchesBtc(t) - }, longEventuallyWaitTimeOut, eventuallyPollTime) - - // At this point babylon is inline with btc. Now: - // Kill reporter - // and generate reorg on btc - // start reporter again - // Even though reorg happened, reporter should be able to provide better chain - // in bootstrap phase - - vigilantReporter.Stop() - vigilantReporter.WaitForShutdown() - - // // we will start from block before tip and submit 2 new block this should trigger rollback - tm.GenerateAndSubmitBlockNBlockStartingFromDepth(t, 2, 1) - - btcClient := initBTCClientWithSubscriber(t, tm.Config) //current tm.btcClient already has an active zmq subscription, would panic - defer btcClient.Stop() - - // Start new reporter - vigilantReporterNew, err := reporter.New( - &tm.Config.Reporter, - logger, - btcClient, - tm.BabylonClient, - btcNotifier, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - reporterMetrics, - ) - require.NoError(t, err) - - vigilantReporterNew.Start() - - // Headers should match even though reorg happened - require.Eventually(t, func() bool { - return tm.BabylonBTCChainMatchesBtc(t) - }, longEventuallyWaitTimeOut, eventuallyPollTime) -} diff --git a/e2etest/slasher_e2e_test.go b/e2etest/slasher_e2e_test.go deleted file mode 100644 index c8e36bb..0000000 --- a/e2etest/slasher_e2e_test.go +++ /dev/null @@ -1,290 +0,0 @@ -//go:build e2e -// +build e2e - -package e2etest - -import ( - "go.uber.org/zap" - "testing" - "time" - - "github.com/btcsuite/btcd/chaincfg" - - "github.com/babylonlabs-io/vigilante/btcclient" - bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" - "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/metrics" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/stretchr/testify/require" -) - -func TestSlasher_GracefulShutdown(t *testing.T) { - t.Parallel() - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - emptyHintCache := btcclient.EmptyHintCache{} - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &emptyHintCache, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - backend, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - - go bsTracker.Start() - - // wait for bootstrapping - time.Sleep(10 * time.Second) - - tm.BTCClient.Stop() - // gracefully shut down - defer bsTracker.Stop() -} - -func TestSlasher_Slasher(t *testing.T) { - t.Parallel() - // segwit is activated at height 300. It's needed by staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, 5) - defer tm.Stop(t) - // start WebSocket connection with Babylon for subscriber services - err := tm.BabylonClient.Start() - require.NoError(t, err) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - emptyHintCache := btcclient.EmptyHintCache{} - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &emptyHintCache, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - backend, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - go bsTracker.Start() - defer bsTracker.Stop() - - // wait for bootstrapping - time.Sleep(5 * time.Second) - - // set up a finality provider - _, fpSK := tm.CreateFinalityProvider(t) - // set up a BTC delegation - stakingSlashingInfo, _, _ := tm.CreateBTCDelegation(t, fpSK) - - // commit public randomness, vote and equivocate - tm.VoteAndEquivocate(t, fpSK) - - // slashing tx will eventually enter mempool - slashingMsgTx, err := stakingSlashingInfo.SlashingTx.ToMsgTx() - require.NoError(t, err) - slashingMsgTxHash1 := slashingMsgTx.TxHash() - slashingMsgTxHash := &slashingMsgTxHash1 - - // mine a block that includes slashing tx - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingMsgTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - minedBlock := tm.mineBlock(t) - // ensure 2 txs will eventually be received (staking tx and slashing tx) - require.Equal(t, 2, len(minedBlock.Transactions)) -} - -func TestSlasher_SlashingUnbonding(t *testing.T) { - t.Parallel() - // segwit is activated at height 300. It's needed by staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, 5) - defer tm.Stop(t) - // start WebSocket connection with Babylon for subscriber services - err := tm.BabylonClient.Start() - require.NoError(t, err) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - emptyHintCache := btcclient.EmptyHintCache{} - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &emptyHintCache, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - backend, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - go bsTracker.Start() - defer bsTracker.Stop() - - // wait for bootstrapping - time.Sleep(5 * time.Second) - - // set up a finality provider - _, fpSK := tm.CreateFinalityProvider(t) - // set up a BTC delegation - _, _, _ = tm.CreateBTCDelegation(t, fpSK) - // set up a BTC delegation - stakingSlashingInfo1, unbondingSlashingInfo1, stakerPrivKey1 := tm.CreateBTCDelegation(t, fpSK) - - // undelegate - unbondingSlashingInfo, _ := tm.Undelegate(t, stakingSlashingInfo1, unbondingSlashingInfo1, stakerPrivKey1, func() { tm.CatchUpBTCLightClient(t) }) - - // commit public randomness, vote and equivocate - tm.VoteAndEquivocate(t, fpSK) - - // slashing tx will eventually enter mempool - unbondingSlashingMsgTx, err := unbondingSlashingInfo.SlashingTx.ToMsgTx() - require.NoError(t, err) - unbondingSlashingMsgTxHash1 := unbondingSlashingMsgTx.TxHash() - unbondingSlashingMsgTxHash := &unbondingSlashingMsgTxHash1 - - // slash unbonding tx will eventually enter mempool - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(unbondingSlashingMsgTxHash) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // mine a block that includes slashing tx - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingSlashingMsgTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - tm.mineBlock(t) - - // ensure tx is eventually on Bitcoin - require.Eventually(t, func() bool { - res, err := tm.BTCClient.GetRawTransactionVerbose(unbondingSlashingMsgTxHash) - if err != nil { - return false - } - return len(res.BlockHash) > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) -} - -func TestSlasher_Bootstrapping(t *testing.T) { - t.Parallel() - // segwit is activated at height 300. It's needed by staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, 5) - defer tm.Stop(t) - // start WebSocket connection with Babylon for subscriber services - err := tm.BabylonClient.Start() - require.NoError(t, err) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - // set up a finality provider - _, fpSK := tm.CreateFinalityProvider(t) - // set up a BTC delegation - stakingSlashingInfo, _, _ := tm.CreateBTCDelegation(t, fpSK) - - // commit public randomness, vote and equivocate - tm.VoteAndEquivocate(t, fpSK) - - emptyHintCache := btcclient.EmptyHintCache{} - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &emptyHintCache, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - backend, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - - // bootstrap BTC staking tracker - err = bsTracker.Bootstrap(0) - require.NoError(t, err) - - // slashing tx will eventually enter mempool - slashingMsgTx, err := stakingSlashingInfo.SlashingTx.ToMsgTx() - require.NoError(t, err) - slashingMsgTxHash1 := slashingMsgTx.TxHash() - slashingMsgTxHash := &slashingMsgTxHash1 - - // mine a block that includes slashing tx - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingMsgTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - minedBlock := tm.mineBlock(t) - // ensure 2 txs will eventually be received (staking tx and slashing tx) - require.Equal(t, 2, len(minedBlock.Transactions)) -} diff --git a/e2etest/submitter_e2e_test.go b/e2etest/submitter_e2e_test.go deleted file mode 100644 index 81dc024..0000000 --- a/e2etest/submitter_e2e_test.go +++ /dev/null @@ -1,195 +0,0 @@ -//go:build e2e -// +build e2e - -package e2etest - -import ( - "github.com/babylonlabs-io/vigilante/testutil" - promtestutil "github.com/prometheus/client_golang/prometheus/testutil" - "math/rand" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" - checkpointingtypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg/chainhash" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - - "github.com/babylonlabs-io/vigilante/metrics" - "github.com/babylonlabs-io/vigilante/submitter" -) - -func TestSubmitterSubmission(t *testing.T) { - t.Parallel() - r := rand.New(rand.NewSource(time.Now().Unix())) - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - - randomCheckpoint := datagen.GenRandomRawCheckpointWithMeta(r) - randomCheckpoint.Status = checkpointingtypes.Sealed - randomCheckpoint.Ckpt.EpochNum = 1 - - ctl := gomock.NewController(t) - mockBabylonClient := submitter.NewMockBabylonQueryClient(ctl) - subAddr, _ := sdk.AccAddressFromBech32(submitterAddrStr) - - mockBabylonClient.EXPECT().BTCCheckpointParams().Return( - &btcctypes.QueryParamsResponse{ - Params: btcctypes.Params{ - CheckpointTag: babylonTagHex, - BtcConfirmationDepth: 2, - CheckpointFinalizationTimeout: 4, - }, - }, nil) - mockBabylonClient.EXPECT().RawCheckpointList(gomock.Any(), gomock.Any()).Return( - &checkpointingtypes.QueryRawCheckpointListResponse{ - RawCheckpoints: []*checkpointingtypes.RawCheckpointWithMetaResponse{ - randomCheckpoint.ToResponse(), - }, - }, nil).AnyTimes() - - tm.Config.Submitter.PollingIntervalSeconds = 2 - - // create submitter - vigilantSubmitter, _ := submitter.New( - &tm.Config.Submitter, - logger, - tm.BTCClient, - mockBabylonClient, - subAddr, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - tm.Config.Common.MaxRetryTimes, - metrics.NewSubmitterMetrics(), - testutil.MakeTestBackend(t), - tm.Config.BTC.WalletName, - ) - - vigilantSubmitter.Start() - - defer func() { - vigilantSubmitter.Stop() - vigilantSubmitter.WaitForShutdown() - }() - - // wait for our 2 op_returns with epoch 1 checkpoint to hit the mempool - var mempoolTxs []*chainhash.Hash - require.Eventually(t, func() bool { - var err error - mempoolTxs, err = tm.BTCClient.GetRawMempool() - require.NoError(t, err) - return len(mempoolTxs) > 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - require.NotNil(t, mempoolTxs) - - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, mempoolTxs)) == 2 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // mine a block with those transactions - blockWithOpReturnTransactions := tm.mineBlock(t) - // block should have 3 transactions, 2 from submitter and 1 coinbase - require.Equal(t, len(blockWithOpReturnTransactions.Transactions), 3) -} - -func TestSubmitterSubmissionReplace(t *testing.T) { - t.Parallel() - r := rand.New(rand.NewSource(time.Now().Unix())) - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - - randomCheckpoint := datagen.GenRandomRawCheckpointWithMeta(r) - randomCheckpoint.Status = checkpointingtypes.Sealed - randomCheckpoint.Ckpt.EpochNum = 1 - - ctl := gomock.NewController(t) - mockBabylonClient := submitter.NewMockBabylonQueryClient(ctl) - subAddr, _ := sdk.AccAddressFromBech32(submitterAddrStr) - - mockBabylonClient.EXPECT().BTCCheckpointParams().Return( - &btcctypes.QueryParamsResponse{ - Params: btcctypes.Params{ - CheckpointTag: babylonTagHex, - BtcConfirmationDepth: 2, - CheckpointFinalizationTimeout: 4, - }, - }, nil) - mockBabylonClient.EXPECT().RawCheckpointList(gomock.Any(), gomock.Any()).Return( - &checkpointingtypes.QueryRawCheckpointListResponse{ - RawCheckpoints: []*checkpointingtypes.RawCheckpointWithMetaResponse{ - randomCheckpoint.ToResponse(), - }, - }, nil).AnyTimes() - - tm.Config.Submitter.PollingIntervalSeconds = 2 - tm.Config.Submitter.ResendIntervalSeconds = 2 - tm.Config.Submitter.ResubmitFeeMultiplier = 2.1 - // create submitter - vigilantSubmitter, _ := submitter.New( - &tm.Config.Submitter, - logger, - tm.BTCClient, - mockBabylonClient, - subAddr, - tm.Config.Common.RetrySleepTime, - tm.Config.Common.MaxRetrySleepTime, - tm.Config.Common.MaxRetryTimes, - metrics.NewSubmitterMetrics(), - testutil.MakeTestBackend(t), - tm.Config.BTC.WalletName, - ) - - vigilantSubmitter.Start() - - defer func() { - vigilantSubmitter.Stop() - vigilantSubmitter.WaitForShutdown() - }() - - // wait for our 2 op_returns with epoch 1 checkpoint to hit the mempool and then - // retrieve them from there - txsMap := make(map[string]struct{}) - var sendTransactions []*btcutil.Tx - - var mempoolTxs []*chainhash.Hash - require.Eventually(t, func() bool { - var err error - mempoolTxs, err = tm.BTCClient.GetRawMempool() - require.NoError(t, err) - for _, hash := range mempoolTxs { - hashStr := hash.String() - if _, exists := txsMap[hashStr]; !exists { - tx, err := tm.BTCClient.GetRawTransaction(hash) - require.NoError(t, err) - txsMap[hashStr] = struct{}{} - sendTransactions = append(sendTransactions, tx) - } - } - return len(txsMap) == 3 - }, eventuallyWaitTimeOut, 50*time.Millisecond) - - resendTx2 := sendTransactions[2] - - // Here check that sendTransactions1 are replacements for sendTransactions, i.e they should have: - // 1. same - // 2. outputs with different values - // 3. different signatures - require.Equal(t, sendTransactions[1].MsgTx().TxIn[0].PreviousOutPoint, resendTx2.MsgTx().TxIn[0].PreviousOutPoint) - require.Less(t, resendTx2.MsgTx().TxOut[1].Value, sendTransactions[1].MsgTx().TxOut[1].Value) - require.NotEqual(t, sendTransactions[1].MsgTx().TxIn[0].Witness[0], resendTx2.MsgTx().TxIn[0].Witness[0]) - - // mine a block with those replacement transactions just to be sure they execute correctly - blockWithOpReturnTransactions := tm.mineBlock(t) - // block should have 2 transactions, 1 from submitter and 1 coinbase - require.Equal(t, len(blockWithOpReturnTransactions.Transactions), 3) - require.True(t, promtestutil.ToFloat64(vigilantSubmitter.Metrics().ResentCheckpointsCounter) == 1) -} diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 16df8a2..548c809 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -2,31 +2,24 @@ package e2etest import ( "bytes" - "context" "encoding/hex" - "encoding/json" "fmt" - "github.com/babylonlabs-io/vigilante/e2etest/container" - "github.com/btcsuite/btcd/txscript" - pv "github.com/cosmos/relayer/v2/relayer/provider" - "go.uber.org/zap" - "os" "path/filepath" "testing" "time" + "github.com/babylonlabs-io/babylon-staking-indexer/e2etest/container" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/clients/btcclient" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/config" bbnclient "github.com/babylonlabs-io/babylon/client/client" - bbn "github.com/babylonlabs-io/babylon/types" - btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" - "github.com/babylonlabs-io/vigilante/btcclient" - "github.com/babylonlabs-io/vigilante/config" - "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcutil" + bbncfg "github.com/babylonlabs-io/babylon/client/config" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcutil" + "github.com/ltcsuite/ltcd/btcec" "github.com/stretchr/testify/require" + "google.golang.org/appengine/log" ) var ( @@ -40,45 +33,14 @@ var ( defaultEpochInterval = uint(400) //nolint:unused ) -func defaultVigilanteConfig() *config.Config { - defaultConfig := config.DefaultConfig() - defaultConfig.BTC.NetParams = regtestParams.Name - defaultConfig.BTC.Endpoint = "127.0.0.1:18443" - // Config setting necessary to connect btcwallet daemon - defaultConfig.BTC.WalletPassword = "pass" - defaultConfig.BTC.Username = "user" - defaultConfig.BTC.Password = "pass" - defaultConfig.BTC.ZmqSeqEndpoint = config.DefaultZmqSeqEndpoint - - return defaultConfig -} - type TestManager struct { - TestRpcClient *rpcclient.Client BitcoindHandler *BitcoindTestHandler BabylonClient *bbnclient.Client - BTCClient *btcclient.Client - Config *config.Config + BTCClient *btcclient.BTCClient + WalletClient *rpcclient.Client WalletPrivKey *btcec.PrivateKey - manger *container.Manager -} - -func initBTCClientWithSubscriber(t *testing.T, cfg *config.Config) *btcclient.Client { - client, err := btcclient.NewWallet(cfg, zap.NewNop()) - require.NoError(t, err) - - // let's wait until chain rpc becomes available - // poll time is increase here to avoid spamming the rpc server - require.Eventually(t, func() bool { - if _, err := client.GetBlockCount(); err != nil { - log.Errorf("failed to get best block: %v", err) - return false - } - - return true - }, eventuallyWaitTimeOut, eventuallyPollTime) - - return client + Config *config.Config + manager *container.Manager } // StartManager creates a test manager @@ -92,14 +54,14 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u passphrase := "pass" _ = btcHandler.CreateWallet("default", passphrase) - cfg := defaultVigilanteConfig() + cfg := DefaultStakingIndexerConfig() - cfg.BTC.Endpoint = fmt.Sprintf("127.0.0.1:%s", bitcoind.GetPort("18443/tcp")) + cfg.BTC.RPCHost = fmt.Sprintf("127.0.0.1:%s", bitcoind.GetPort("18443/tcp")) - testRpcClient, err := rpcclient.New(&rpcclient.ConnConfig{ - Host: cfg.BTC.Endpoint, - User: cfg.BTC.Username, - Pass: cfg.BTC.Password, + walletClient, err := rpcclient.New(&rpcclient.ConnConfig{ + Host: cfg.BTC.RPCHost, + User: cfg.BTC.RPCUser, + Pass: cfg.BTC.RPCPass, DisableTLS: true, DisableConnectOnNew: true, DisableAutoReconnect: false, @@ -107,15 +69,13 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u }, nil) require.NoError(t, err) - err = testRpcClient.WalletPassphrase(passphrase, 600) + err = walletClient.WalletPassphrase(passphrase, 600) require.NoError(t, err) walletPrivKey, err := importPrivateKey(btcHandler) require.NoError(t, err) blocksResponse := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) - btcClient := initBTCClientWithSubscriber(t, cfg) - var buff bytes.Buffer err = regtestParams.GenesisBlock.Header.Serialize(&buff) require.NoError(t, err) @@ -135,16 +95,18 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u babylond, err := manager.RunBabylondResource(t, tmpDir, baseHeaderHex, hex.EncodeToString(pkScript), epochInterval) require.NoError(t, err) + defaultBbnCfg := bbncfg.DefaultBabylonConfig() + // create Babylon client - cfg.Babylon.KeyDirectory = filepath.Join(tmpDir, "node0", "babylond") - cfg.Babylon.Key = "test-spending-key" // keyring to bbn node - cfg.Babylon.GasAdjustment = 3.0 + defaultBbnCfg.KeyDirectory = filepath.Join(tmpDir, "node0", "babylond") + defaultBbnCfg.Key = "test-spending-key" // keyring to bbn node + defaultBbnCfg.GasAdjustment = 3.0 // update port with the dynamically allocated one from docker - cfg.Babylon.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) - cfg.Babylon.GRPCAddr = fmt.Sprintf("https://localhost:%s", babylond.GetPort("9090/tcp")) + defaultBbnCfg.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) + defaultBbnCfg.GRPCAddr = fmt.Sprintf("https://localhost:%s", babylond.GetPort("9090/tcp")) - babylonClient, err := bbnclient.New(&cfg.Babylon, nil) + babylonClient, err := bbnclient.New(&defaultBbnCfg, nil) require.NoError(t, err) // wait until Babylon is ready @@ -157,14 +119,19 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u return true }, eventuallyWaitTimeOut, eventuallyPollTime) + btcClient, err := btcclient.NewBTCClient( + &cfg.BTC, + ) + require.NoError(t, err) + return &TestManager{ - TestRpcClient: testRpcClient, + WalletClient: walletClient, BabylonClient: babylonClient, BitcoindHandler: btcHandler, BTCClient: btcClient, Config: cfg, WalletPrivKey: walletPrivKey, - manger: manager, + manager: manager, } } @@ -175,117 +142,24 @@ func (tm *TestManager) Stop(t *testing.T) { } } -// mineBlock mines a single block -func (tm *TestManager) mineBlock(t *testing.T) *wire.MsgBlock { - resp := tm.BitcoindHandler.GenerateBlocks(1) - - hash, err := chainhash.NewHashFromStr(resp.Blocks[0]) - require.NoError(t, err) - - header, err := tm.TestRpcClient.GetBlock(hash) - require.NoError(t, err) - - return header -} - -func (tm *TestManager) MustGetBabylonSigner() string { - return tm.BabylonClient.MustGetAddr() -} - -// RetrieveTransactionFromMempool fetches transactions from the mempool for the given hashes -func (tm *TestManager) RetrieveTransactionFromMempool(t *testing.T, hashes []*chainhash.Hash) []*btcutil.Tx { - var txs []*btcutil.Tx - for _, txHash := range hashes { - tx, err := tm.BTCClient.GetRawTransaction(txHash) - require.NoError(t, err) - txs = append(txs, tx) - } - - return txs -} - -func (tm *TestManager) InsertBTCHeadersToBabylon(headers []*wire.BlockHeader) (*pv.RelayerTxResponse, error) { - var headersBytes []bbn.BTCHeaderBytes - - for _, h := range headers { - headersBytes = append(headersBytes, bbn.NewBTCHeaderBytesFromBlockHeader(h)) - } - - msg := btclctypes.MsgInsertHeaders{ - Headers: headersBytes, - Signer: tm.MustGetBabylonSigner(), - } - - return tm.BabylonClient.InsertHeaders(context.Background(), &msg) -} - -func (tm *TestManager) CatchUpBTCLightClient(t *testing.T) { - btcHeight, err := tm.TestRpcClient.GetBlockCount() - require.NoError(t, err) - - tipResp, err := tm.BabylonClient.BTCHeaderChainTip() - require.NoError(t, err) - btclcHeight := tipResp.Header.Height - - var headers []*wire.BlockHeader - for i := int(btclcHeight + 1); i <= int(btcHeight); i++ { - hash, err := tm.TestRpcClient.GetBlockHash(int64(i)) - require.NoError(t, err) - header, err := tm.TestRpcClient.GetBlockHeader(hash) - require.NoError(t, err) - headers = append(headers, header) - } - - _, err = tm.InsertBTCHeadersToBabylon(headers) - require.NoError(t, err) -} - -func importPrivateKey(btcHandler *BitcoindTestHandler) (*btcec.PrivateKey, error) { - privKey, err := btcec.NewPrivateKey() - if err != nil { - return nil, err - } - - wif, err := btcutil.NewWIF(privKey, regtestParams, true) - if err != nil { - return nil, err - } - - // "combo" allows us to import a key and handle multiple types of btc scripts with a single descriptor command. - descriptor := fmt.Sprintf("combo(%s)", wif.String()) - - // Create the JSON descriptor object. - descJSON, err := json.Marshal([]map[string]interface{}{ - { - "desc": descriptor, - "active": true, - "timestamp": "now", // tells Bitcoind to start scanning from the current blockchain height - "label": "test key", - }, - }) - - if err != nil { - return nil, err - } - - btcHandler.ImportDescriptors(string(descJSON)) +func DefaultStakingIndexerConfig() *config.Config { + defaultConfig := config.DefaultConfig() - return privKey, nil -} + // enable emitting extra events for testing + //defaultConfig.ExtraEventEnabled = true -func tempDir(t *testing.T) (string, error) { - tempPath, err := os.MkdirTemp(os.TempDir(), "babylon-test-*") - if err != nil { - return "", err - } + // both wallet and node are bitcoind + defaultConfig.BTC.NetParams = regtestParams.Name - if err = os.Chmod(tempPath, 0777); err != nil { - return "", err - } + bitcoindHost := "127.0.0.1:18443" + bitcoindUser := "user" + bitcoindPass := "pass" - t.Cleanup(func() { - _ = os.RemoveAll(tempPath) - }) + defaultConfig.BTC.RPCHost = bitcoindHost + defaultConfig.BTC.RPCUser = bitcoindUser + defaultConfig.BTC.RPCPass = bitcoindPass + defaultConfig.BTC.BlockPollingInterval = 1 * time.Second + defaultConfig.BTC.TxPollingInterval = 1 * time.Second - return tempPath, err + return defaultConfig } diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go deleted file mode 100644 index 2527e81..0000000 --- a/e2etest/test_manager_btcstaking.go +++ /dev/null @@ -1,898 +0,0 @@ -package e2etest - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "math/rand" - "testing" - "time" - - sdkmath "cosmossdk.io/math" - "github.com/avast/retry-go/v4" - "github.com/babylonlabs-io/babylon-staking-indexer/types" - "github.com/babylonlabs-io/babylon/btcstaking" - txformat "github.com/babylonlabs-io/babylon/btctxformatter" - "github.com/babylonlabs-io/babylon/crypto/eots" - asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" - "github.com/babylonlabs-io/babylon/testutil/datagen" - bbn "github.com/babylonlabs-io/babylon/types" - btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" - btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" - bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" - ftypes "github.com/babylonlabs-io/babylon/x/finality/types" - "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkquery "github.com/cosmos/cosmos-sdk/types/query" - sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/relayer/v2/relayer/provider" - "github.com/stretchr/testify/require" -) - -var ( - r = rand.New(rand.NewSource(time.Now().Unix())) - - // covenant - covenantSk, _ = btcec.PrivKeyFromBytes( - []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - ) -) - -func (tm *TestManager) getBTCUnbondingTime(t *testing.T) uint32 { - bsParams, err := tm.BabylonClient.BTCStakingParams() - require.NoError(t, err) - - return bsParams.Params.UnbondingTimeBlocks -} - -func (tm *TestManager) CreateFinalityProvider(t *testing.T) (*bstypes.FinalityProvider, *btcec.PrivateKey) { - var err error - signerAddr := tm.BabylonClient.MustGetAddr() - addr := sdk.MustAccAddressFromBech32(signerAddr) - - fpSK, _, err := datagen.GenRandomBTCKeyPair(r) - require.NoError(t, err) - btcFp, err := datagen.GenRandomFinalityProviderWithBTCBabylonSKs(r, fpSK, addr) - require.NoError(t, err) - - /* - create finality provider - */ - commission := sdkmath.LegacyZeroDec() - msgNewVal := &bstypes.MsgCreateFinalityProvider{ - Addr: signerAddr, - Description: &stakingtypes.Description{Moniker: datagen.GenRandomHexStr(r, 10)}, - Commission: &commission, - BtcPk: btcFp.BtcPk, - Pop: btcFp.Pop, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgNewVal, nil, nil) - require.NoError(t, err) - - return btcFp, fpSK -} - -func (tm *TestManager) CreateBTCDelegation( - t *testing.T, - fpSK *btcec.PrivateKey, -) (*datagen.TestStakingSlashingInfo, *datagen.TestUnbondingSlashingInfo, *btcec.PrivateKey) { - signerAddr := tm.BabylonClient.MustGetAddr() - addr := sdk.MustAccAddressFromBech32(signerAddr) - - fpPK := fpSK.PubKey() - - /* - create BTC delegation - */ - // generate staking tx and slashing tx - bsParams, err := tm.BabylonClient.BTCStakingParams() - require.NoError(t, err) - covenantBtcPks, err := bbnPksToBtcPks(bsParams.Params.CovenantPks) - require.NoError(t, err) - stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks - // get top UTXO - topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() - require.NoError(t, err) - topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) - require.NoError(t, err) - // staking value - stakingValue := int64(topUTXO.Amount) / 3 - - // generate legitimate BTC del - stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash := tm.createStakingAndSlashingTx(t, fpSK, bsParams, covenantBtcPks, topUTXO, stakingValue, stakingTimeBlocks) - - // send staking tx to Bitcoin node's mempool - _, err = tm.BTCClient.SendRawTransaction(stakingMsgTx, true) - require.NoError(t, err) - - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{stakingMsgTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - mBlock := tm.mineBlock(t) - require.Equal(t, 2, len(mBlock.Transactions)) - - // wait until staking tx is on Bitcoin - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(stakingMsgTxHash) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) - // get spv proof of the BTC staking tx - stakingTxInfo := getTxInfo(t, mBlock) - - // insert k empty blocks to Bitcoin - btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() - require.NoError(t, err) - btccParams := btccParamsResp.Params - for i := 0; i < int(btccParams.BtcConfirmationDepth); i++ { - tm.mineBlock(t) - } - - stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) - require.NoError(t, err) - - // create PoP - pop, err := bstypes.NewPoPBTC(addr, tm.WalletPrivKey) - require.NoError(t, err) - slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() - require.NoError(t, err) - // generate proper delegator sig - require.NoError(t, err) - - delegatorSig, err := stakingSlashingInfo.SlashingTx.Sign( - stakingMsgTx, - stakingOutIdx, - slashingSpendPath.GetPkScriptPath(), - tm.WalletPrivKey, - ) - require.NoError(t, err) - - // Generate all data necessary for unbonding - unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( - t, - fpPK, - bsParams, - covenantBtcPks, - stakingSlashingInfo, - stakingMsgTxHash, - stakingOutIdx, - stakingTimeBlocks, - ) - - tm.CatchUpBTCLightClient(t) - - // Build a message to send - // submit BTC delegation to Babylon - msgBTCDel := &bstypes.MsgCreateBTCDelegation{ - StakerAddr: signerAddr, - Pop: pop, - BtcPk: bbn.NewBIP340PubKeyFromBTCPK(tm.WalletPrivKey.PubKey()), - FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, - StakingTime: stakingTimeBlocks, - StakingValue: stakingValue, - StakingTx: stakingTxInfo.Transaction, - StakingTxInclusionProof: &bstypes.InclusionProof{ - Key: stakingTxInfo.Key, - Proof: stakingTxInfo.Proof, - }, - SlashingTx: stakingSlashingInfo.SlashingTx, - DelegatorSlashingSig: delegatorSig, - // Unbonding related data - UnbondingTime: tm.getBTCUnbondingTime(t), - UnbondingTx: unbondingTxBytes, - UnbondingValue: unbondingSlashingInfo.UnbondingInfo.UnbondingOutput.Value, - UnbondingSlashingTx: unbondingSlashingInfo.SlashingTx, - DelegatorUnbondingSlashingSig: slashingTxSig, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgBTCDel, nil, nil) - require.NoError(t, err) - t.Logf("submitted MsgCreateBTCDelegation") - - // generate and insert new covenant signature, to activate the BTC delegation - tm.addCovenantSig( - t, - signerAddr, - stakingMsgTx, - stakingMsgTxHash, - fpSK, slashingSpendPath, - stakingSlashingInfo, - unbondingSlashingInfo, - unbondingSlashingPathSpendInfo, - stakingOutIdx, - ) - - return stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey -} - -func (tm *TestManager) CreateBTCDelegationWithoutIncl( - t *testing.T, - fpSK *btcec.PrivateKey, -) (*wire.MsgTx, *datagen.TestStakingSlashingInfo, *datagen.TestUnbondingSlashingInfo, *btcec.PrivateKey) { - signerAddr := tm.BabylonClient.MustGetAddr() - addr := sdk.MustAccAddressFromBech32(signerAddr) - - fpPK := fpSK.PubKey() - - /* - create BTC delegation - */ - // generate staking tx and slashing tx - bsParams, err := tm.BabylonClient.BTCStakingParams() - require.NoError(t, err) - covenantBtcPks, err := bbnPksToBtcPks(bsParams.Params.CovenantPks) - require.NoError(t, err) - stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks - // get top UTXO - topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() - require.NoError(t, err) - topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) - require.NoError(t, err) - // staking value - stakingValue := int64(topUTXO.Amount) / 3 - - // generate legitimate BTC del - stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash := tm.createStakingAndSlashingTx(t, fpSK, bsParams, covenantBtcPks, topUTXO, stakingValue, stakingTimeBlocks) - - stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) - require.NoError(t, err) - - // create PoP - pop, err := bstypes.NewPoPBTC(addr, tm.WalletPrivKey) - require.NoError(t, err) - slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() - require.NoError(t, err) - // generate proper delegator sig - require.NoError(t, err) - - delegatorSig, err := stakingSlashingInfo.SlashingTx.Sign( - stakingMsgTx, - stakingOutIdx, - slashingSpendPath.GetPkScriptPath(), - tm.WalletPrivKey, - ) - require.NoError(t, err) - - // Generate all data necessary for unbonding - unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( - t, - fpPK, - bsParams, - covenantBtcPks, - stakingSlashingInfo, - stakingMsgTxHash, - stakingOutIdx, - stakingTimeBlocks, - ) - - var stakingTxBuf bytes.Buffer - err = stakingMsgTx.Serialize(&stakingTxBuf) - require.NoError(t, err) - - // submit BTC delegation to Babylon - msgBTCDel := &bstypes.MsgCreateBTCDelegation{ - StakerAddr: signerAddr, - Pop: pop, - BtcPk: bbn.NewBIP340PubKeyFromBTCPK(tm.WalletPrivKey.PubKey()), - FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, - StakingTime: stakingTimeBlocks, - StakingValue: stakingValue, - StakingTx: stakingTxBuf.Bytes(), - StakingTxInclusionProof: nil, - SlashingTx: stakingSlashingInfo.SlashingTx, - DelegatorSlashingSig: delegatorSig, - // Unbonding related data - UnbondingTime: uint32(tm.getBTCUnbondingTime(t)), - UnbondingTx: unbondingTxBytes, - UnbondingValue: unbondingSlashingInfo.UnbondingInfo.UnbondingOutput.Value, - UnbondingSlashingTx: unbondingSlashingInfo.SlashingTx, - DelegatorUnbondingSlashingSig: slashingTxSig, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgBTCDel, nil, nil) - require.NoError(t, err) - t.Logf("submitted MsgCreateBTCDelegation") - - // generate and insert new covenant signature, to activate the BTC delegation - tm.addCovenantSig( - t, - signerAddr, - stakingMsgTx, - stakingMsgTxHash, - fpSK, slashingSpendPath, - stakingSlashingInfo, - unbondingSlashingInfo, - unbondingSlashingPathSpendInfo, - stakingOutIdx, - ) - - return stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey -} - -func (tm *TestManager) createStakingAndSlashingTx( - t *testing.T, fpSK *btcec.PrivateKey, - bsParams *bstypes.QueryParamsResponse, - covenantBtcPks []*btcec.PublicKey, - topUTXO *types.UTXO, - stakingValue int64, - stakingTimeBlocks uint32, -) (*wire.MsgTx, *datagen.TestStakingSlashingInfo, *chainhash.Hash) { - // generate staking tx and slashing tx - fpPK := fpSK.PubKey() - - // generate legitimate BTC del - stakingSlashingInfo := datagen.GenBTCStakingSlashingInfoWithOutPoint( - r, - t, - regtestParams, - topUTXO.GetOutPoint(), - tm.WalletPrivKey, - []*btcec.PublicKey{fpPK}, - covenantBtcPks, - bsParams.Params.CovenantQuorum, - uint16(stakingTimeBlocks), - stakingValue, - bsParams.Params.SlashingPkScript, - bsParams.Params.SlashingRate, - uint16(tm.getBTCUnbondingTime(t)), - ) - // sign staking tx and overwrite the staking tx to the signed version - // NOTE: the tx hash has changed here since stakingMsgTx is pre-segwit - stakingMsgTx, signed, err := tm.BTCClient.SignRawTransactionWithWallet(stakingSlashingInfo.StakingTx) - require.NoError(t, err) - require.True(t, signed) - // overwrite staking tx - stakingSlashingInfo.StakingTx = stakingMsgTx - // get signed staking tx hash - stakingMsgTxHash1 := stakingSlashingInfo.StakingTx.TxHash() - stakingMsgTxHash := &stakingMsgTxHash1 - t.Logf("signed staking tx hash: %s", stakingMsgTxHash.String()) - - // change outpoint tx hash of slashing tx to the txhash of the signed staking tx - slashingMsgTx, err := stakingSlashingInfo.SlashingTx.ToMsgTx() - require.NoError(t, err) - slashingMsgTx.TxIn[0].PreviousOutPoint.Hash = stakingSlashingInfo.StakingTx.TxHash() - // update slashing tx - stakingSlashingInfo.SlashingTx, err = bstypes.NewBTCSlashingTxFromMsgTx(slashingMsgTx) - require.NoError(t, err) - - return stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash -} - -func (tm *TestManager) createUnbondingData( - t *testing.T, - fpPK *btcec.PublicKey, - bsParams *bstypes.QueryParamsResponse, - covenantBtcPks []*btcec.PublicKey, - stakingSlashingInfo *datagen.TestStakingSlashingInfo, - stakingMsgTxHash *chainhash.Hash, - stakingOutIdx uint32, - stakingTimeBlocks uint32, -) (*datagen.TestUnbondingSlashingInfo, *btcstaking.SpendInfo, []byte, *bbn.BIP340Signature) { - fee := int64(1000) - unbondingValue := stakingSlashingInfo.StakingInfo.StakingOutput.Value - fee - unbondingSlashingInfo := datagen.GenBTCUnbondingSlashingInfo( - r, - t, - regtestParams, - tm.WalletPrivKey, - []*btcec.PublicKey{fpPK}, - covenantBtcPks, - bsParams.Params.CovenantQuorum, - wire.NewOutPoint(stakingMsgTxHash, stakingOutIdx), - uint16(stakingTimeBlocks), - unbondingValue, - bsParams.Params.SlashingPkScript, - bsParams.Params.SlashingRate, - uint16(tm.getBTCUnbondingTime(t)), - ) - unbondingTxBytes, err := bbn.SerializeBTCTx(unbondingSlashingInfo.UnbondingTx) - require.NoError(t, err) - - unbondingSlashingPathSpendInfo, err := unbondingSlashingInfo.UnbondingInfo.SlashingPathSpendInfo() - require.NoError(t, err) - slashingTxSig, err := unbondingSlashingInfo.SlashingTx.Sign( - unbondingSlashingInfo.UnbondingTx, - 0, // Only one output in the unbonding tx - unbondingSlashingPathSpendInfo.GetPkScriptPath(), - tm.WalletPrivKey, - ) - require.NoError(t, err) - - return unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig -} - -func (tm *TestManager) addCovenantSig( - t *testing.T, - signerAddr string, - stakingMsgTx *wire.MsgTx, - stakingMsgTxHash *chainhash.Hash, - fpSK *btcec.PrivateKey, - slashingSpendPath *btcstaking.SpendInfo, - stakingSlashingInfo *datagen.TestStakingSlashingInfo, - unbondingSlashingInfo *datagen.TestUnbondingSlashingInfo, - unbondingSlashingPathSpendInfo *btcstaking.SpendInfo, - stakingOutIdx uint32, -) { - // TODO: Make this handle multiple covenant signatures - fpEncKey, err := asig.NewEncryptionKeyFromBTCPK(fpSK.PubKey()) - require.NoError(t, err) - covenantSig, err := stakingSlashingInfo.SlashingTx.EncSign( - stakingMsgTx, - stakingOutIdx, - slashingSpendPath.GetPkScriptPath(), - covenantSk, - fpEncKey, - ) - require.NoError(t, err) - // TODO: Add covenant sigs for all covenants - // add covenant sigs - // covenant Schnorr sig on unbonding tx - unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() - require.NoError(t, err) - unbondingTxCovenantSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( - unbondingSlashingInfo.UnbondingTx, - stakingSlashingInfo.StakingTx, - stakingOutIdx, - unbondingPathSpendInfo.GetPkScriptPath(), - covenantSk, - ) - require.NoError(t, err) - covenantUnbondingSig := bbn.NewBIP340SignatureFromBTCSig(unbondingTxCovenantSchnorrSig) - // covenant adaptor sig on unbonding slashing tx - require.NoError(t, err) - covenantSlashingSig, err := unbondingSlashingInfo.SlashingTx.EncSign( - unbondingSlashingInfo.UnbondingTx, - 0, // Only one output in the unbonding transaction - unbondingSlashingPathSpendInfo.GetPkScriptPath(), - covenantSk, - fpEncKey, - ) - require.NoError(t, err) - msgAddCovenantSig := &bstypes.MsgAddCovenantSigs{ - Signer: signerAddr, - Pk: bbn.NewBIP340PubKeyFromBTCPK(covenantSk.PubKey()), - StakingTxHash: stakingMsgTxHash.String(), - SlashingTxSigs: [][]byte{covenantSig.MustMarshal()}, - UnbondingTxSig: covenantUnbondingSig, - SlashingUnbondingTxSigs: [][]byte{covenantSlashingSig.MustMarshal()}, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgAddCovenantSig, nil, nil) - require.NoError(t, err) - t.Logf("submitted covenant signature") -} - -func (tm *TestManager) Undelegate( - t *testing.T, - stakingSlashingInfo *datagen.TestStakingSlashingInfo, - unbondingSlashingInfo *datagen.TestUnbondingSlashingInfo, - delSK *btcec.PrivateKey, - catchUpLightClientFunc func()) (*datagen.TestUnbondingSlashingInfo, *schnorr.Signature) { - signerAddr := tm.BabylonClient.MustGetAddr() - - // TODO: This generates unbonding tx signature, move it to undelegate - unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() - require.NoError(t, err) - - // the only input to unbonding tx is the staking tx - stakingOutIdx, err := outIdx(unbondingSlashingInfo.UnbondingTx, unbondingSlashingInfo.UnbondingInfo.UnbondingOutput) - require.NoError(t, err) - unbondingTxSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( - unbondingSlashingInfo.UnbondingTx, - stakingSlashingInfo.StakingTx, - stakingOutIdx, - unbondingPathSpendInfo.GetPkScriptPath(), - delSK, - ) - require.NoError(t, err) - - var unbondingTxBuf bytes.Buffer - err = unbondingSlashingInfo.UnbondingTx.Serialize(&unbondingTxBuf) - require.NoError(t, err) - - resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) - require.NoError(t, err) - covenantSigs := resp.BtcDelegation.UndelegationResponse.CovenantUnbondingSigList - witness, err := unbondingPathSpendInfo.CreateUnbondingPathWitness( - []*schnorr.Signature{covenantSigs[0].Sig.MustToBTCSig()}, - unbondingTxSchnorrSig, - ) - require.NoError(t, err) - unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness - - // send unbonding tx to Bitcoin node's mempool - unbondingTxHash, err := tm.BTCClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) - require.NoError(t, err) - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(unbondingTxHash) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) - t.Logf("submitted unbonding tx with hash %s", unbondingTxHash.String()) - - // mine a block with this tx, and insert it to Bitcoin - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - mBlock := tm.mineBlock(t) - require.Equal(t, 2, len(mBlock.Transactions)) - - catchUpLightClientFunc() - - unbondingTxInfo := getTxInfo(t, mBlock) - msgUndel := &bstypes.MsgBTCUndelegate{ - Signer: signerAddr, - StakingTxHash: stakingSlashingInfo.StakingTx.TxHash().String(), - StakeSpendingTx: unbondingTxBuf.Bytes(), - StakeSpendingTxInclusionProof: &bstypes.InclusionProof{ - Key: unbondingTxInfo.Key, - Proof: unbondingTxInfo.Proof, - }, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgUndel, nil, nil) - require.NoError(t, err) - t.Logf("submitted MsgBTCUndelegate") - - // wait until unbonding tx is on Bitcoin - require.Eventually(t, func() bool { - resp, err := tm.BTCClient.GetRawTransactionVerbose(unbondingTxHash) - if err != nil { - t.Logf("err of GetRawTransactionVerbose: %v", err) - return false - } - return len(resp.BlockHash) > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - return unbondingSlashingInfo, unbondingTxSchnorrSig -} - -func (tm *TestManager) VoteAndEquivocate(t *testing.T, fpSK *btcec.PrivateKey) { - signerAddr := tm.BabylonClient.MustGetAddr() - - // get the finality provider - fpBTCPK := bbn.NewBIP340PubKeyFromBTCPK(fpSK.PubKey()) - fpResp, err := tm.BabylonClient.FinalityProvider(fpBTCPK.MarshalHex()) - require.NoError(t, err) - btcFp := fpResp.FinalityProvider - - _, err = tm.BabylonClient.ActivatedHeight() - require.Error(t, err) - - activatedHeight := uint64(1) - commitStartHeight := activatedHeight - - /* - commit a number of public randomness since activatedHeight - */ - srList, msgCommitPubRandList, err := datagen.GenRandomMsgCommitPubRandList(r, fpSK, activatedHeight, 100) - require.NoError(t, err) - msgCommitPubRandList.Signer = signerAddr - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgCommitPubRandList, nil, nil) - require.NoError(t, err) - t.Logf("committed public randomness") - - tm.mineBlock(t) - - tm.waitForFpPubRandTimestamped(t, fpSK.PubKey()) - - require.Eventually(t, func() bool { - acr, err := tm.BabylonClient.ActivatedHeight() - if err != nil { - return false - } - activatedHeight = acr.Height - return activatedHeight > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - /* - submit finality signature - */ - // get block to vote - blockToVote, err := tm.BabylonClient.GetBlock(int64(activatedHeight)) - require.NoError(t, err) - msgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), blockToVote.Block.AppHash...) - // generate EOTS signature - idx := activatedHeight - commitStartHeight - sig, err := eots.Sign(fpSK, srList.SRList[idx], msgToSign) - require.NoError(t, err) - eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig) - // submit finality signature - msgAddFinalitySig := &ftypes.MsgAddFinalitySig{ - Signer: signerAddr, - FpBtcPk: btcFp.BtcPk, - BlockHeight: activatedHeight, - PubRand: &srList.PRList[idx], - Proof: srList.ProofList[idx].ToProto(), - BlockAppHash: blockToVote.Block.AppHash, - FinalitySig: eotsSig, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgAddFinalitySig, nil, nil) - require.NoError(t, err) - t.Logf("submitted finality signature") - - /* - equivocate - */ - invalidAppHash := datagen.GenRandomByteArray(r, 32) - invalidMsgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), invalidAppHash...) - invalidSig, err := eots.Sign(fpSK, srList.SRList[idx], invalidMsgToSign) - require.NoError(t, err) - invalidEotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(invalidSig) - invalidMsgAddFinalitySig := &ftypes.MsgAddFinalitySig{ - Signer: signerAddr, - FpBtcPk: btcFp.BtcPk, - BlockHeight: activatedHeight, - PubRand: &srList.PRList[idx], - Proof: srList.ProofList[idx].ToProto(), - BlockAppHash: invalidAppHash, - FinalitySig: invalidEotsSig, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), invalidMsgAddFinalitySig, nil, nil) - require.NoError(t, err) - t.Logf("submitted equivocating finality signature") -} - -func getTxInfo(t *testing.T, block *wire.MsgBlock) *btcctypes.TransactionInfo { - mHeaderBytes := bbn.NewBTCHeaderBytesFromBlockHeader(&block.Header) - var txBytes [][]byte - for _, tx := range block.Transactions { - buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) - _ = tx.Serialize(buf) - txBytes = append(txBytes, buf.Bytes()) - } - spvProof, err := btcctypes.SpvProofFromHeaderAndTransactions(&mHeaderBytes, txBytes, 1) - require.NoError(t, err) - return btcctypes.NewTransactionInfoFromSpvProof(spvProof) -} - -// TODO: these functions should be enabled by Babylon -func bbnPksToBtcPks(pks []bbn.BIP340PubKey) ([]*btcec.PublicKey, error) { - btcPks := make([]*btcec.PublicKey, 0, len(pks)) - for _, pk := range pks { - btcPk, err := pk.ToBTCPK() - if err != nil { - return nil, err - } - btcPks = append(btcPks, btcPk) - } - return btcPks, nil -} - -func outIdx(tx *wire.MsgTx, candOut *wire.TxOut) (uint32, error) { - for idx, out := range tx.TxOut { - if bytes.Equal(out.PkScript, candOut.PkScript) && out.Value == candOut.Value { - return uint32(idx), nil - } - } - return 0, fmt.Errorf("couldn't find output") -} - -func (tm *TestManager) waitForFpPubRandTimestamped(t *testing.T, fpPk *btcec.PublicKey) { - var lastCommittedHeight uint64 - var err error - - require.Eventually(t, func() bool { - lastCommittedHeight, err = tm.getLastCommittedHeight(fpPk) - if err != nil { - return false - } - return lastCommittedHeight > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - t.Logf("public randomness is successfully committed, last committed height: %d", lastCommittedHeight) - - // wait until the last registered epoch is finalized - currentEpoch, err := tm.BabylonClient.CurrentEpoch() - require.NoError(t, err) - - tm.finalizeUntilEpoch(t, currentEpoch.CurrentEpoch) - - res, err := tm.BabylonClient.LatestEpochFromStatus(ckpttypes.Finalized) - require.NoError(t, err) - t.Logf("last finalized epoch: %d", res.RawCheckpoint.EpochNum) - - t.Logf("public randomness is successfully timestamped, last finalized epoch: %v", currentEpoch) -} - -// queryLastCommittedPublicRand returns the last public randomness commitments -func (tm *TestManager) queryLastCommittedPublicRand(fpPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { - fpBtcPk := bbn.NewBIP340PubKeyFromBTCPK(fpPk) - - pagination := &sdkquery.PageRequest{ - Limit: count, - Reverse: true, - } - - res, err := tm.BabylonClient.QueryClient.ListPubRandCommit(fpBtcPk.MarshalHex(), pagination) - if err != nil { - return nil, fmt.Errorf("failed to query committed public randomness: %w", err) - } - - return res.PubRandCommitMap, nil -} - -func (tm *TestManager) lastCommittedPublicRandWithRetry(btcPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { - var response map[uint64]*ftypes.PubRandCommitResponse - - if err := retry.Do(func() error { - resp, err := tm.queryLastCommittedPublicRand(btcPk, count) - if err != nil { - return err - } - response = resp - return nil - }, - retry.Attempts(tm.Config.Common.MaxRetryTimes), - retry.Delay(tm.Config.Common.RetrySleepTime), - retry.LastErrorOnly(true)); err != nil { - return nil, err - } - - return response, nil -} - -func (tm *TestManager) getLastCommittedHeight(btcPk *btcec.PublicKey) (uint64, error) { - pubRandCommitMap, err := tm.lastCommittedPublicRandWithRetry(btcPk, 1) - if err != nil { - return 0, err - } - - // no committed randomness yet - if len(pubRandCommitMap) == 0 { - return 0, nil - } - - if len(pubRandCommitMap) > 1 { - return 0, fmt.Errorf("got more than one last committed public randomness") - } - var lastCommittedHeight uint64 - for startHeight, resp := range pubRandCommitMap { - lastCommittedHeight = startHeight + resp.NumPubRand - 1 - } - - return lastCommittedHeight, nil -} - -func (tm *TestManager) finalizeUntilEpoch(t *testing.T, epoch uint64) { - bbnClient := tm.BabylonClient - - // wait until the checkpoint of this epoch is sealed - require.Eventually(t, func() bool { - lastSealedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Sealed) - if err != nil { - return false - } - return epoch <= lastSealedCkpt.RawCheckpoint.EpochNum - }, eventuallyWaitTimeOut, 1*time.Second) - - t.Logf("start finalizing epochs until %d", epoch) - // Random source for the generation of BTC data - r := rand.New(rand.NewSource(time.Now().Unix())) - - // get all checkpoints of these epochs - pagination := &sdkquerytypes.PageRequest{ - Key: ckpttypes.CkptsObjectKey(0), - Limit: epoch, - } - resp, err := bbnClient.RawCheckpoints(pagination) - require.NoError(t, err) - require.Equal(t, int(epoch), len(resp.RawCheckpoints)) - - submitterAddr, err := sdk.AccAddressFromBech32(tm.BabylonClient.MustGetAddr()) - require.NoError(t, err) - - for _, checkpoint := range resp.RawCheckpoints { - currentBtcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() - require.NoError(t, err) - tipHeader, err := bbn.NewBTCHeaderBytesFromHex(currentBtcTipResp.Header.HeaderHex) - require.NoError(t, err) - - rawCheckpoint, err := checkpoint.Ckpt.ToRawCheckpoint() - require.NoError(t, err) - - btcCheckpoint, err := ckpttypes.FromRawCkptToBTCCkpt(rawCheckpoint, submitterAddr) - require.NoError(t, err) - - babylonTagBytes, err := hex.DecodeString("01020304") - require.NoError(t, err) - - p1, p2, err := txformat.EncodeCheckpointData( - babylonTagBytes, - txformat.CurrentVersion, - btcCheckpoint, - ) - require.NoError(t, err) - - tx1 := datagen.CreatOpReturnTransaction(r, p1) - - opReturn1 := datagen.CreateBlockWithTransaction(r, tipHeader.ToBlockHeader(), tx1) - tx2 := datagen.CreatOpReturnTransaction(r, p2) - opReturn2 := datagen.CreateBlockWithTransaction(r, opReturn1.HeaderBytes.ToBlockHeader(), tx2) - - // insert headers and proofs - _, err = tm.insertBtcBlockHeaders([]bbn.BTCHeaderBytes{ - opReturn1.HeaderBytes, - opReturn2.HeaderBytes, - }) - require.NoError(t, err) - - _, err = tm.insertSpvProofs(submitterAddr.String(), []*btcctypes.BTCSpvProof{ - opReturn1.SpvProof, - opReturn2.SpvProof, - }) - require.NoError(t, err) - - // wait until this checkpoint is submitted - require.Eventually(t, func() bool { - ckpt, err := bbnClient.RawCheckpoint(checkpoint.Ckpt.EpochNum) - if err != nil { - return false - } - return ckpt.RawCheckpoint.Status == ckpttypes.Submitted - }, eventuallyWaitTimeOut, eventuallyPollTime) - } - - // insert w BTC headers - tm.insertWBTCHeaders(t, r) - - // wait until the checkpoint of this epoch is finalised - require.Eventually(t, func() bool { - lastFinalizedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Finalized) - if err != nil { - t.Logf("failed to get last finalized epoch: %v", err) - return false - } - return epoch <= lastFinalizedCkpt.RawCheckpoint.EpochNum - }, eventuallyWaitTimeOut, 1*time.Second) - - t.Logf("epoch %d is finalised", epoch) -} - -func (tm *TestManager) insertBtcBlockHeaders(headers []bbn.BTCHeaderBytes) (*provider.RelayerTxResponse, error) { - msg := &btclctypes.MsgInsertHeaders{ - Signer: tm.MustGetBabylonSigner(), - Headers: headers, - } - - res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) - if err != nil { - return nil, err - } - - return res, nil -} - -func (tm *TestManager) insertSpvProofs(submitter string, proofs []*btcctypes.BTCSpvProof) (*provider.RelayerTxResponse, error) { - msg := &btcctypes.MsgInsertBTCSpvProof{ - Submitter: submitter, - Proofs: proofs, - } - - res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) - if err != nil { - return nil, err - } - - return res, nil -} - -func (tm *TestManager) insertWBTCHeaders(t *testing.T, r *rand.Rand) { - ckptParamRes, err := tm.BabylonClient.QueryClient.BTCCheckpointParams() - require.NoError(t, err) - btcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() - require.NoError(t, err) - tipHeader, err := bbn.NewBTCHeaderBytesFromHex(btcTipResp.Header.HeaderHex) - require.NoError(t, err) - kHeaders := datagen.NewBTCHeaderChainFromParentInfo(r, &btclctypes.BTCHeaderInfo{ - Header: &tipHeader, - Hash: tipHeader.Hash(), - Height: btcTipResp.Header.Height, - Work: &btcTipResp.Header.Work, - }, uint32(ckptParamRes.Params.CheckpointFinalizationTimeout)) - _, err = tm.insertBtcBlockHeaders(kHeaders.ChainToBytes()) - require.NoError(t, err) -} diff --git a/e2etest/unbondingwatcher_e2e_test.go b/e2etest/unbondingwatcher_e2e_test.go deleted file mode 100644 index 80cbde8..0000000 --- a/e2etest/unbondingwatcher_e2e_test.go +++ /dev/null @@ -1,336 +0,0 @@ -//go:build e2e -// +build e2e - -package e2etest - -import ( - "fmt" - btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - promtestutil "github.com/prometheus/client_golang/prometheus/testutil" - "go.uber.org/zap" - "sync" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/btcstaking" - "github.com/babylonlabs-io/vigilante/btcclient" - bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" - "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/metrics" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/stretchr/testify/require" -) - -func TestUnbondingWatcher(t *testing.T) { - t.Parallel() - // segwit is activated at height 300. It's needed by staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - emptyHintCache := btcclient.EmptyHintCache{} - - backend, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &emptyHintCache, - ) - require.NoError(t, err) - - err = backend.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - backend, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - bsTracker.Start() - defer bsTracker.Stop() - - // set up a finality provider - _, fpSK := tm.CreateFinalityProvider(t) - // set up a BTC delegation - stakingSlashingInfo, unbondingSlashingInfo, delSK := tm.CreateBTCDelegation(t, fpSK) - - // Staker unbonds by directly sending tx to btc network. Watcher should detect it and report to babylon. - unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() - require.NoError(t, err) - stakingOutIdx, err := outIdx(unbondingSlashingInfo.UnbondingTx, unbondingSlashingInfo.UnbondingInfo.UnbondingOutput) - require.NoError(t, err) - - unbondingTxSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( - unbondingSlashingInfo.UnbondingTx, - stakingSlashingInfo.StakingTx, - stakingOutIdx, - unbondingPathSpendInfo.GetPkScriptPath(), - delSK, - ) - require.NoError(t, err) - - resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) - require.NoError(t, err) - - covenantSigs := resp.BtcDelegation.UndelegationResponse.CovenantUnbondingSigList - witness, err := unbondingPathSpendInfo.CreateUnbondingPathWitness( - []*schnorr.Signature{covenantSigs[0].Sig.MustToBTCSig()}, - unbondingTxSchnorrSig, - ) - unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness - - // Send unbonding tx to Bitcoin - _, err = tm.BTCClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) - require.NoError(t, err) - - // mine a block with this tx, and insert it to Bitcoin - unbondingTxHash := unbondingSlashingInfo.UnbondingTx.TxHash() - t.Logf("submitted unbonding tx with hash %s", unbondingTxHash.String()) - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&unbondingTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - minedBlock := tm.mineBlock(t) - require.Equal(t, 2, len(minedBlock.Transactions)) - - tm.CatchUpBTCLightClient(t) - - require.Eventually(t, func() bool { - resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) - require.NoError(t, err) - - // TODO: Add field for staker signature in BTCDelegation query to check it directly, - // for now it is enough to check that delegation is not active, as if unbonding was reported - // delegation will be deactivated - return !resp.BtcDelegation.Active - - }, eventuallyWaitTimeOut, eventuallyPollTime) -} - -// TestActivatingDelegation verifies that a delegation created without an inclusion proof will -// eventually become "active". -// Specifically, that stakingEventWatcher will send a MsgAddBTCDelegationInclusionProof to do so. -func TestActivatingDelegation(t *testing.T) { - t.Parallel() - // segwit is activated at height 300. It's necessary for staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - btcNotifier, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &btcclient.EmptyHintCache{}, - ) - require.NoError(t, err) - - err = btcNotifier.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - btcNotifier, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - bsTracker.Start() - defer bsTracker.Stop() - - // set up a finality provider - _, fpSK := tm.CreateFinalityProvider(t) - // set up a BTC delegation - stakingMsgTx, stakingSlashingInfo, _, _ := tm.CreateBTCDelegationWithoutIncl(t, fpSK) - stakingMsgTxHash := stakingMsgTx.TxHash() - - // send staking tx to Bitcoin node's mempool - _, err = tm.BTCClient.SendRawTransaction(stakingMsgTx, true) - require.NoError(t, err) - - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&stakingMsgTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - mBlock := tm.mineBlock(t) - require.Equal(t, 2, len(mBlock.Transactions)) - - // wait until staking tx is on Bitcoin - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(&stakingMsgTxHash) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - // We want to introduce a latency to make sure that we are not trying to submit inclusion proof while the - // staking tx is not yet K-deep - time.Sleep(10 * time.Second) - // Insert k empty blocks to Bitcoin - btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() - if err != nil { - fmt.Println("Error fetching BTCCheckpointParams:", err) - return - } - for i := 0; i < int(btccParamsResp.Params.BtcConfirmationDepth); i++ { - tm.mineBlock(t) - } - tm.CatchUpBTCLightClient(t) - }() - - wg.Wait() - - // make sure we didn't submit any "invalid" incl proof - require.Eventually(t, func() bool { - return promtestutil.ToFloat64(stakingTrackerMetrics.FailedReportedActivateDelegations) == 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // created delegation lacks inclusion proof, once created it will be in - // pending status, once convenant signatures are added it will be in verified status, - // and once the stakingEventWatcher submits MsgAddBTCDelegationInclusionProof it will - // be in active status - require.Eventually(t, func() bool { - resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) - require.NoError(t, err) - - return resp.BtcDelegation.Active - }, eventuallyWaitTimeOut, eventuallyPollTime) -} - -// TestActivatingAndUnbondingDelegation tests that delegation will eventually become UNBONDED given that -// both staking and unbonding tx are in the same block. -// In this test, we include both staking tx and unbonding tx in the same block. -// The delegation goes through "VERIFIED" → "ACTIVE" → "UNBONDED" status throughout this test. -func TestActivatingAndUnbondingDelegation(t *testing.T) { - //t.Parallel() - // segwit is activated at height 300. It's necessary for staking/slashing tx - numMatureOutputs := uint32(300) - - tm := StartManager(t, numMatureOutputs, defaultEpochInterval) - defer tm.Stop(t) - // Insert all existing BTC headers to babylon node - tm.CatchUpBTCLightClient(t) - - btcNotifier, err := btcclient.NewNodeBackend( - btcclient.ToBitcoindConfig(tm.Config.BTC), - &chaincfg.RegressionNetParams, - &btcclient.EmptyHintCache{}, - ) - require.NoError(t, err) - - err = btcNotifier.Start() - require.NoError(t, err) - - commonCfg := config.DefaultCommonConfig() - bstCfg := config.DefaultBTCStakingTrackerConfig() - bstCfg.CheckDelegationsInterval = 1 * time.Second - stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() - - bsTracker := bst.NewBTCStakingTracker( - tm.BTCClient, - btcNotifier, - tm.BabylonClient, - &bstCfg, - &commonCfg, - zap.NewNop(), - stakingTrackerMetrics, - ) - bsTracker.Start() - defer bsTracker.Stop() - - // set up a finality provider - _, fpSK := tm.CreateFinalityProvider(t) - // set up a BTC delegation - stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, delSK := tm.CreateBTCDelegationWithoutIncl(t, fpSK) - stakingMsgTxHash := stakingMsgTx.TxHash() - - // send staking tx to Bitcoin node's mempool - _, err = tm.BTCClient.SendRawTransaction(stakingMsgTx, true) - require.NoError(t, err) - - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&stakingMsgTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // Staker unbonds by directly sending tx to btc network. Watcher should detect it and report to babylon. - unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() - require.NoError(t, err) - stakingOutIdx, err := outIdx(unbondingSlashingInfo.UnbondingTx, unbondingSlashingInfo.UnbondingInfo.UnbondingOutput) - require.NoError(t, err) - - unbondingTxSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( - unbondingSlashingInfo.UnbondingTx, - stakingSlashingInfo.StakingTx, - stakingOutIdx, - unbondingPathSpendInfo.GetPkScriptPath(), - delSK, - ) - require.NoError(t, err) - - resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) - require.NoError(t, err) - - covenantSigs := resp.BtcDelegation.UndelegationResponse.CovenantUnbondingSigList - witness, err := unbondingPathSpendInfo.CreateUnbondingPathWitness( - []*schnorr.Signature{covenantSigs[0].Sig.MustToBTCSig()}, - unbondingTxSchnorrSig, - ) - require.NoError(t, err) - unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness - - // Send unbonding tx to Bitcoin - _, err = tm.BTCClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) - require.NoError(t, err) - - unbondingTxHash := unbondingSlashingInfo.UnbondingTx.TxHash() - t.Logf("submitted unbonding tx with hash %s", unbondingTxHash.String()) - require.Eventually(t, func() bool { - return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&unbondingTxHash})) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - mBlock := tm.mineBlock(t) - // both staking and unbonding txs are in this block - require.Equal(t, 3, len(mBlock.Transactions)) - - // insert k empty blocks to Bitcoin - btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() - require.NoError(t, err) - btccParams := btccParamsResp.Params - for i := 0; i < int(btccParams.BtcConfirmationDepth); i++ { - tm.mineBlock(t) - } - - tm.CatchUpBTCLightClient(t) - - // wait until delegation has become unbonded - require.Eventually(t, func() bool { - resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) - require.NoError(t, err) - - return resp.BtcDelegation.StatusDesc == btcstakingtypes.BTCDelegationStatus_UNBONDED.String() - }, eventuallyWaitTimeOut, eventuallyPollTime) -} diff --git a/internal/config/btc.go b/internal/config/btc.go index 14fa5d4..ee2cd81 100644 --- a/internal/config/btc.go +++ b/internal/config/btc.go @@ -82,3 +82,32 @@ func (cfg *BTCConfig) Validate() error { return nil } + +const ( + // default rpc port of signet is 38332 + defaultBitcoindRpcHost = "127.0.0.1:38332" + defaultBitcoindRPCUser = "user" + defaultBitcoindRPCPass = "pass" + defaultBitcoindBlockCacheSize = 20 * 1024 * 1024 // 20 MB + defaultBlockPollingInterval = 30 * time.Second + defaultTxPollingInterval = 30 * time.Second + defaultMaxRetryTimes = 5 + defaultRetryInterval = 500 * time.Millisecond + // DefaultTxPollingJitter defines the default TxPollingIntervalJitter + // to be used for bitcoind backend. + DefaultTxPollingJitter = 0.5 +) + +func DefaultBTCConfig() *BTCConfig { + return &BTCConfig{ + RPCHost: defaultBitcoindRpcHost, + RPCUser: defaultBitcoindRPCUser, + RPCPass: defaultBitcoindRPCPass, + BlockPollingInterval: defaultBlockPollingInterval, + TxPollingInterval: defaultTxPollingInterval, + BlockCacheSize: defaultBitcoindBlockCacheSize, + MaxRetryTimes: defaultMaxRetryTimes, + RetryInterval: defaultRetryInterval, + NetParams: "regtest", + } +} diff --git a/internal/config/config.go b/internal/config/config.go index 399746e..68ca0b1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "strings" + "time" queue "github.com/babylonlabs-io/staking-queue-client/config" "github.com/spf13/viper" @@ -81,3 +82,23 @@ func New(cfgFile string) (*Config, error) { return &cfg, nil } + +func DefaultConfig() *Config { + cfg := &Config{ + BTC: *DefaultBTCConfig(), + Db: *DefaultDBConfig(), + Poller: PollerConfig{ + ParamPollingInterval: 1 * time.Second, + ExpiryCheckerPollingInterval: 1 * time.Second, + ExpiredDelegationsLimit: 1000, + }, + Queue: *queue.DefaultQueueConfig(), + Metrics: DefaultMetricsConfig(), + } + + if err := cfg.Validate(); err != nil { + panic(err) + } + + return cfg +} diff --git a/internal/config/db.go b/internal/config/db.go index 03fbc94..403f933 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -59,3 +59,20 @@ func (cfg *DbConfig) Validate() error { return nil } + +const ( + defaultDbAddress = "mongodb://localhost:27017" + defaultDbUsername = "user" + defaultDbPassword = "pass" + defaultDbName = "staking" +) + +func DefaultDBConfig() *DbConfig { + return &DbConfig{ + Address: defaultDbAddress, + Username: defaultDbUsername, + Password: defaultDbPassword, + DbName: defaultDbName, + } + +} From aebd067475bba261c44e846aaf59c2a77a95eaf1 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 16:39:10 +0530 Subject: [PATCH 06/32] fix --- e2etest/test_manager.go | 73 ++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 548c809..7e11907 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "os" "path/filepath" "testing" "time" @@ -13,13 +14,14 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/config" bbnclient "github.com/babylonlabs-io/babylon/client/client" bbncfg "github.com/babylonlabs-io/babylon/client/config" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/ltcsuite/ltcd/btcec" "github.com/stretchr/testify/require" - "google.golang.org/appengine/log" ) var ( @@ -53,37 +55,28 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u bitcoind := btcHandler.Start(t) passphrase := "pass" _ = btcHandler.CreateWallet("default", passphrase) + resp := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) + minerAddressDecoded, err := btcutil.DecodeAddress(resp.Address, regtestParams) + require.NoError(t, err) cfg := DefaultStakingIndexerConfig() cfg.BTC.RPCHost = fmt.Sprintf("127.0.0.1:%s", bitcoind.GetPort("18443/tcp")) - walletClient, err := rpcclient.New(&rpcclient.ConnConfig{ - Host: cfg.BTC.RPCHost, - User: cfg.BTC.RPCUser, - Pass: cfg.BTC.RPCPass, - DisableTLS: true, - DisableConnectOnNew: true, - DisableAutoReconnect: false, - HTTPPostMode: true, - }, nil) + connCfg, err := cfg.BTC.ToConnConfig() require.NoError(t, err) - - err = walletClient.WalletPassphrase(passphrase, 600) + rpcclient, err := rpcclient.New(connCfg, nil) require.NoError(t, err) - - walletPrivKey, err := importPrivateKey(btcHandler) + err = rpcclient.WalletPassphrase(passphrase, 200) + require.NoError(t, err) + walletPrivKey, err := rpcclient.DumpPrivKey(minerAddressDecoded) require.NoError(t, err) - blocksResponse := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) var buff bytes.Buffer err = regtestParams.GenesisBlock.Header.Serialize(&buff) require.NoError(t, err) baseHeaderHex := hex.EncodeToString(buff.Bytes()) - minerAddressDecoded, err := btcutil.DecodeAddress(blocksResponse.Address, regtestParams) - require.NoError(t, err) - pkScript, err := txscript.PayToAddrScript(minerAddressDecoded) require.NoError(t, err) @@ -111,11 +104,11 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u // wait until Babylon is ready require.Eventually(t, func() bool { - resp, err := babylonClient.CurrentEpoch() + _, err := babylonClient.CurrentEpoch() if err != nil { return false } - log.Infof("Babylon is ready: %v", resp) + //log.Infof("Babylon is ready: %v", resp) return true }, eventuallyWaitTimeOut, eventuallyPollTime) @@ -125,12 +118,12 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u require.NoError(t, err) return &TestManager{ - WalletClient: walletClient, + WalletClient: rpcclient, BabylonClient: babylonClient, BitcoindHandler: btcHandler, BTCClient: btcClient, Config: cfg, - WalletPrivKey: walletPrivKey, + WalletPrivKey: walletPrivKey.PrivKey, manager: manager, } } @@ -142,6 +135,40 @@ func (tm *TestManager) Stop(t *testing.T) { } } +// mineBlock mines a single block +func (tm *TestManager) mineBlock(t *testing.T) *wire.MsgBlock { + resp := tm.BitcoindHandler.GenerateBlocks(1) + + hash, err := chainhash.NewHashFromStr(resp.Blocks[0]) + require.NoError(t, err) + + header, err := tm.WalletClient.GetBlock(hash) + require.NoError(t, err) + + return header +} + +func (tm *TestManager) MustGetBabylonSigner() string { + return tm.BabylonClient.MustGetAddr() +} + +func tempDir(t *testing.T) (string, error) { + tempPath, err := os.MkdirTemp(os.TempDir(), "babylon-test-*") + if err != nil { + return "", err + } + + if err = os.Chmod(tempPath, 0777); err != nil { + return "", err + } + + t.Cleanup(func() { + _ = os.RemoveAll(tempPath) + }) + + return tempPath, err +} + func DefaultStakingIndexerConfig() *config.Config { defaultConfig := config.DefaultConfig() From ab6df58737fca0cd2dc42bd75e019f6067033d83 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 16:46:52 +0530 Subject: [PATCH 07/32] rm testutil --- testutil/datagen/datagen.go | 18 -- testutil/datagen/reporter.go | 271 --------------------- testutil/mocks/btcclient.go | 448 ----------------------------------- testutil/port.go | 61 ----- testutil/store.go | 26 -- testutil/version.go | 32 --- 6 files changed, 856 deletions(-) delete mode 100644 testutil/datagen/datagen.go delete mode 100644 testutil/datagen/reporter.go delete mode 100644 testutil/mocks/btcclient.go delete mode 100644 testutil/port.go delete mode 100644 testutil/store.go delete mode 100644 testutil/version.go diff --git a/testutil/datagen/datagen.go b/testutil/datagen/datagen.go deleted file mode 100644 index d38e45d..0000000 --- a/testutil/datagen/datagen.go +++ /dev/null @@ -1,18 +0,0 @@ -package datagen - -import ( - "math/rand" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - "github.com/babylonlabs-io/vigilante/types" -) - -func GenerateRandomCheckpointRecord(r *rand.Rand) *types.CheckpointRecord { - rawCheckpoint := datagen.GenRandomRawCheckpoint(r) - btcHeight := datagen.RandomIntOtherThan(r, 0, 1000) - - return &types.CheckpointRecord{ - RawCheckpoint: rawCheckpoint, - FirstSeenBtcHeight: uint32(btcHeight), - } -} diff --git a/testutil/datagen/reporter.go b/testutil/datagen/reporter.go deleted file mode 100644 index f627c05..0000000 --- a/testutil/datagen/reporter.go +++ /dev/null @@ -1,271 +0,0 @@ -package datagen - -import ( - "math" - "math/big" - "math/rand" - - "github.com/babylonlabs-io/babylon/btctxformatter" - "github.com/babylonlabs-io/babylon/testutil/datagen" - babylontypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/vigilante/types" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" -) - -// calcMerkleRoot creates a merkle tree from the slice of transactions and -// returns the root of the tree. -// (taken from https://github.com/btcsuite/btcd/blob/master/blockchain/fullblocktests/generate.go) -func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash { - if len(txns) == 0 { - return chainhash.Hash{} - } - - utilTxns := make([]*btcutil.Tx, 0, len(txns)) - for _, tx := range txns { - utilTxns = append(utilTxns, btcutil.NewTx(tx)) - } - merkles := blockchain.BuildMerkleTreeStore(utilTxns, false) - return *merkles[len(merkles)-1] -} - -func GetRandomRawBtcCheckpoint(r *rand.Rand) *btctxformatter.RawBtcCheckpoint { - return &btctxformatter.RawBtcCheckpoint{ - Epoch: r.Uint64(), - BlockHash: datagen.GenRandomByteArray(r, btctxformatter.BlockHashLength), - BitMap: datagen.GenRandomByteArray(r, btctxformatter.BitMapLength), - SubmitterAddress: datagen.GenRandomByteArray(r, btctxformatter.AddressLength), - BlsSig: datagen.GenRandomByteArray(r, btctxformatter.BlsSigLength), - } -} - -func GenRandomTx(r *rand.Rand) *wire.MsgTx { - // structure of the below tx is from https://github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go - tx := &wire.MsgTx{ - Version: 1, - TxIn: []*wire.TxIn{ - { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.HashH(datagen.GenRandomByteArray(r, 10)), - Index: r.Uint32(), - }, - SignatureScript: datagen.GenRandomByteArray(r, 10), - Sequence: r.Uint32(), - }, - }, - TxOut: []*wire.TxOut{ - { - Value: r.Int63(), - PkScript: datagen.GenRandomByteArray(r, 80), - }, - }, - LockTime: 0, - } - - return tx -} - -func GenRandomBabylonTxPair(r *rand.Rand) ([]*wire.MsgTx, *btctxformatter.RawBtcCheckpoint) { - txs := []*wire.MsgTx{GenRandomTx(r), GenRandomTx(r)} - builder := txscript.NewScriptBuilder() - - // fake a raw checkpoint - rawBTCCkpt := GetRandomRawBtcCheckpoint(r) - // encode raw checkpoint to two halves - firstHalf, secondHalf, err := btctxformatter.EncodeCheckpointData( - []byte{1, 2, 3, 4}, - btctxformatter.CurrentVersion, - rawBTCCkpt, - ) - if err != nil { - panic(err) - } - - dataScript1, err := builder.AddOp(txscript.OP_RETURN).AddData(firstHalf).Script() - if err != nil { - panic(err) - } - txs[0].TxOut[0] = wire.NewTxOut(0, dataScript1) - - // reset builder - builder = txscript.NewScriptBuilder() - - dataScript2, err := builder.AddOp(txscript.OP_RETURN).AddData(secondHalf).Script() - if err != nil { - panic(err) - } - txs[1].TxOut[0] = wire.NewTxOut(0, dataScript2) - - return txs, rawBTCCkpt -} - -func GenRandomBabylonTx(r *rand.Rand) *wire.MsgTx { - tx := GenRandomTx(r) - builder := txscript.NewScriptBuilder() - - // fake a raw checkpoint - rawBTCCkpt := GetRandomRawBtcCheckpoint(r) - // encode raw checkpoint to two halves - firstHalf, secondHalf, err := btctxformatter.EncodeCheckpointData( - []byte{1, 2, 3, 4}, - btctxformatter.CurrentVersion, - rawBTCCkpt, - ) - if err != nil { - panic(err) - } - idx := r.Intn(2) - if idx == 0 { - dataScript, err := builder.AddOp(txscript.OP_RETURN).AddData(firstHalf).Script() - if err != nil { - panic(err) - } - tx.TxOut[0] = wire.NewTxOut(0, dataScript) - } else { - dataScript, err := builder.AddOp(txscript.OP_RETURN).AddData(secondHalf).Script() - if err != nil { - panic(err) - } - tx.TxOut[0] = wire.NewTxOut(0, dataScript) - } - - return tx -} - -func GenRandomBlock(r *rand.Rand, numBabylonTxs int, prevHash *chainhash.Hash) (*wire.MsgBlock, *btctxformatter.RawBtcCheckpoint) { - // create a tx, which will be a Babylon tx with probability `percentage` - var ( - randomTxs []*wire.MsgTx - rawCkpt *btctxformatter.RawBtcCheckpoint - ) - - switch numBabylonTxs { - case 1: - randomTxs, _ = GenRandomBabylonTxPair(r) - randomTxs[1] = GenRandomTx(r) - rawCkpt = nil - case 2: - randomTxs, rawCkpt = GenRandomBabylonTxPair(r) - default: - randomTxs = []*wire.MsgTx{GenRandomTx(r), GenRandomTx(r)} - rawCkpt = nil - } - - coinbaseTx := GenRandomTx(r) - msgTxs := []*wire.MsgTx{coinbaseTx} - msgTxs = append(msgTxs, randomTxs...) - - // calculate correct Merkle root - merkleRoot := calcMerkleRoot(msgTxs) - // don't apply any difficulty - difficulty, _ := new(big.Int).SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16) - workBits := blockchain.BigToCompact(difficulty) - - // find a header that satisfies difficulty - var header *wire.BlockHeader - for { - header = datagen.GenRandomBtcdHeader(r) - header.MerkleRoot = merkleRoot - header.Bits = workBits - - if prevHash == nil { - header.PrevBlock = chainhash.DoubleHashH(datagen.GenRandomByteArray(r, 10)) - } else { - header.PrevBlock = *prevHash - } - - if err := babylontypes.ValidateBTCHeader(header, chaincfg.SimNetParams.PowLimit); err == nil { - break - } - } - - block := &wire.MsgBlock{ - Header: *header, - Transactions: msgTxs, - } - return block, rawCkpt -} - -// GetRandomIndexedBlocks generates a random number of indexed blocks with a random root height -func GetRandomIndexedBlocks(r *rand.Rand, numBlocks uint64) []*types.IndexedBlock { - var ibs []*types.IndexedBlock - - if numBlocks == 0 { - return ibs - } - - block, _ := GenRandomBlock(r, 1, nil) - prevHeight := r.Int31n(math.MaxInt32 - int32(numBlocks)) - ib := types.NewIndexedBlockFromMsgBlock(uint32(prevHeight), block) - prevHash := ib.Header.BlockHash() - - ibs = GetRandomIndexedBlocksFromHeight(r, numBlocks-1, prevHeight, prevHash) - ibs = append([]*types.IndexedBlock{ib}, ibs...) - return ibs -} - -// GetRandomIndexedBlocksFromHeight generates a random number of indexed blocks with a given root height and root hash -func GetRandomIndexedBlocksFromHeight(r *rand.Rand, numBlocks uint64, rootHeight int32, rootHash chainhash.Hash) []*types.IndexedBlock { - var ( - ibs []*types.IndexedBlock - prevHash = rootHash - prevHeight = uint32(rootHeight) - ) - - for i := 0; i < int(numBlocks); i++ { - block, _ := GenRandomBlock(r, 1, &prevHash) - newIb := types.NewIndexedBlockFromMsgBlock(prevHeight+1, block) - ibs = append(ibs, newIb) - - prevHeight = newIb.Height - prevHash = newIb.Header.BlockHash() - } - - return ibs -} - -func GenRandomBlockchainWithBabylonTx(r *rand.Rand, n uint64, partialPercentage float32, fullPercentage float32) ([]*wire.MsgBlock, int, []*btctxformatter.RawBtcCheckpoint) { - blocks := []*wire.MsgBlock{} - numCkptSegs := 0 - rawCkpts := []*btctxformatter.RawBtcCheckpoint{} - // percentage should be [0, 1] - if partialPercentage < 0 || partialPercentage > 1 { - return blocks, 0, rawCkpts - } - if fullPercentage < 0 || fullPercentage > 1 { - return blocks, 0, rawCkpts - } - // n should be > 0 - if n == 0 { - return blocks, 0, rawCkpts - } - - // genesis block - genesisBlock, rawCkpt := GenRandomBlock(r, 0, nil) - blocks = append(blocks, genesisBlock) - rawCkpts = append(rawCkpts, rawCkpt) - - // blocks after genesis - for i := uint64(1); i < n; i++ { - var msgBlock *wire.MsgBlock - prevHash := blocks[len(blocks)-1].BlockHash() - switch { - case r.Float32() < partialPercentage: - msgBlock, rawCkpt = GenRandomBlock(r, 1, &prevHash) - numCkptSegs++ - case r.Float32() < partialPercentage+fullPercentage: - msgBlock, rawCkpt = GenRandomBlock(r, 2, &prevHash) - numCkptSegs += 2 - default: - msgBlock, rawCkpt = GenRandomBlock(r, 0, &prevHash) - } - - blocks = append(blocks, msgBlock) - rawCkpts = append(rawCkpts, rawCkpt) - } - return blocks, numCkptSegs, rawCkpts -} diff --git a/testutil/mocks/btcclient.go b/testutil/mocks/btcclient.go deleted file mode 100644 index 008c3a5..0000000 --- a/testutil/mocks/btcclient.go +++ /dev/null @@ -1,448 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: btcclient/interface.go - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - btcclient "github.com/babylonlabs-io/vigilante/btcclient" - config "github.com/babylonlabs-io/vigilante/config" - types "github.com/babylonlabs-io/vigilante/types" - btcjson "github.com/btcsuite/btcd/btcjson" - btcutil "github.com/btcsuite/btcd/btcutil" - chaincfg "github.com/btcsuite/btcd/chaincfg" - chainhash "github.com/btcsuite/btcd/chaincfg/chainhash" - wire "github.com/btcsuite/btcd/wire" - gomock "github.com/golang/mock/gomock" - chainntnfs "github.com/lightningnetwork/lnd/chainntnfs" -) - -// MockBTCClient is a mock of BTCClient interface. -type MockBTCClient struct { - ctrl *gomock.Controller - recorder *MockBTCClientMockRecorder -} - -// MockBTCClientMockRecorder is the mock recorder for MockBTCClient. -type MockBTCClientMockRecorder struct { - mock *MockBTCClient -} - -// NewMockBTCClient creates a new mock instance. -func NewMockBTCClient(ctrl *gomock.Controller) *MockBTCClient { - mock := &MockBTCClient{ctrl: ctrl} - mock.recorder = &MockBTCClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBTCClient) EXPECT() *MockBTCClientMockRecorder { - return m.recorder -} - -// FindTailBlocksByHeight mocks base method. -func (m *MockBTCClient) FindTailBlocksByHeight(height uint32) ([]*types.IndexedBlock, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindTailBlocksByHeight", height) - ret0, _ := ret[0].([]*types.IndexedBlock) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindTailBlocksByHeight indicates an expected call of FindTailBlocksByHeight. -func (mr *MockBTCClientMockRecorder) FindTailBlocksByHeight(height interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindTailBlocksByHeight", reflect.TypeOf((*MockBTCClient)(nil).FindTailBlocksByHeight), height) -} - -// GetBestBlock mocks base method. -func (m *MockBTCClient) GetBestBlock() (uint32, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBestBlock") - ret0, _ := ret[0].(uint32) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetBestBlock indicates an expected call of GetBestBlock. -func (mr *MockBTCClientMockRecorder) GetBestBlock() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBestBlock", reflect.TypeOf((*MockBTCClient)(nil).GetBestBlock)) -} - -// GetBlockByHash mocks base method. -func (m *MockBTCClient) GetBlockByHash(blockHash *chainhash.Hash) (*types.IndexedBlock, *wire.MsgBlock, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBlockByHash", blockHash) - ret0, _ := ret[0].(*types.IndexedBlock) - ret1, _ := ret[1].(*wire.MsgBlock) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetBlockByHash indicates an expected call of GetBlockByHash. -func (mr *MockBTCClientMockRecorder) GetBlockByHash(blockHash interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockByHash", reflect.TypeOf((*MockBTCClient)(nil).GetBlockByHash), blockHash) -} - -// GetBlockByHeight mocks base method. -func (m *MockBTCClient) GetBlockByHeight(height uint32) (*types.IndexedBlock, *wire.MsgBlock, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBlockByHeight", height) - ret0, _ := ret[0].(*types.IndexedBlock) - ret1, _ := ret[1].(*wire.MsgBlock) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetBlockByHeight indicates an expected call of GetBlockByHeight. -func (mr *MockBTCClientMockRecorder) GetBlockByHeight(height interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockByHeight", reflect.TypeOf((*MockBTCClient)(nil).GetBlockByHeight), height) -} - -// GetRawTransaction mocks base method. -func (m *MockBTCClient) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRawTransaction", txHash) - ret0, _ := ret[0].(*btcutil.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetRawTransaction indicates an expected call of GetRawTransaction. -func (mr *MockBTCClientMockRecorder) GetRawTransaction(txHash interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRawTransaction", reflect.TypeOf((*MockBTCClient)(nil).GetRawTransaction), txHash) -} - -// GetTransaction mocks base method. -func (m *MockBTCClient) GetTransaction(txHash *chainhash.Hash) (*btcjson.GetTransactionResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTransaction", txHash) - ret0, _ := ret[0].(*btcjson.GetTransactionResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTransaction indicates an expected call of GetTransaction. -func (mr *MockBTCClientMockRecorder) GetTransaction(txHash interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransaction", reflect.TypeOf((*MockBTCClient)(nil).GetTransaction), txHash) -} - -// GetTxOut mocks base method. -func (m *MockBTCClient) GetTxOut(txHash *chainhash.Hash, index uint32, mempool bool) (*btcjson.GetTxOutResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTxOut", txHash, index, mempool) - ret0, _ := ret[0].(*btcjson.GetTxOutResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTxOut indicates an expected call of GetTxOut. -func (mr *MockBTCClientMockRecorder) GetTxOut(txHash, index, mempool interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTxOut", reflect.TypeOf((*MockBTCClient)(nil).GetTxOut), txHash, index, mempool) -} - -// SendRawTransaction mocks base method. -func (m *MockBTCClient) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendRawTransaction", tx, allowHighFees) - ret0, _ := ret[0].(*chainhash.Hash) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SendRawTransaction indicates an expected call of SendRawTransaction. -func (mr *MockBTCClientMockRecorder) SendRawTransaction(tx, allowHighFees interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRawTransaction", reflect.TypeOf((*MockBTCClient)(nil).SendRawTransaction), tx, allowHighFees) -} - -// Stop mocks base method. -func (m *MockBTCClient) Stop() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Stop") -} - -// Stop indicates an expected call of Stop. -func (mr *MockBTCClientMockRecorder) Stop() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockBTCClient)(nil).Stop)) -} - -// TxDetails mocks base method. -func (m *MockBTCClient) TxDetails(txHash *chainhash.Hash, pkScript []byte) (*chainntnfs.TxConfirmation, btcclient.TxStatus, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TxDetails", txHash, pkScript) - ret0, _ := ret[0].(*chainntnfs.TxConfirmation) - ret1, _ := ret[1].(btcclient.TxStatus) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// TxDetails indicates an expected call of TxDetails. -func (mr *MockBTCClientMockRecorder) TxDetails(txHash, pkScript interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TxDetails", reflect.TypeOf((*MockBTCClient)(nil).TxDetails), txHash, pkScript) -} - -// WaitForShutdown mocks base method. -func (m *MockBTCClient) WaitForShutdown() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "WaitForShutdown") -} - -// WaitForShutdown indicates an expected call of WaitForShutdown. -func (mr *MockBTCClientMockRecorder) WaitForShutdown() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForShutdown", reflect.TypeOf((*MockBTCClient)(nil).WaitForShutdown)) -} - -// MockBTCWallet is a mock of BTCWallet interface. -type MockBTCWallet struct { - ctrl *gomock.Controller - recorder *MockBTCWalletMockRecorder -} - -// MockBTCWalletMockRecorder is the mock recorder for MockBTCWallet. -type MockBTCWalletMockRecorder struct { - mock *MockBTCWallet -} - -// NewMockBTCWallet creates a new mock instance. -func NewMockBTCWallet(ctrl *gomock.Controller) *MockBTCWallet { - mock := &MockBTCWallet{ctrl: ctrl} - mock.recorder = &MockBTCWalletMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBTCWallet) EXPECT() *MockBTCWalletMockRecorder { - return m.recorder -} - -// FundRawTransaction mocks base method. -func (m *MockBTCWallet) FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FundRawTransaction", tx, opts, isWitness) - ret0, _ := ret[0].(*btcjson.FundRawTransactionResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FundRawTransaction indicates an expected call of FundRawTransaction. -func (mr *MockBTCWalletMockRecorder) FundRawTransaction(tx, opts, isWitness interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FundRawTransaction", reflect.TypeOf((*MockBTCWallet)(nil).FundRawTransaction), tx, opts, isWitness) -} - -// GetBTCConfig mocks base method. -func (m *MockBTCWallet) GetBTCConfig() *config.BTCConfig { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBTCConfig") - ret0, _ := ret[0].(*config.BTCConfig) - return ret0 -} - -// GetBTCConfig indicates an expected call of GetBTCConfig. -func (mr *MockBTCWalletMockRecorder) GetBTCConfig() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBTCConfig", reflect.TypeOf((*MockBTCWallet)(nil).GetBTCConfig)) -} - -// GetHighUTXOAndSum mocks base method. -func (m *MockBTCWallet) GetHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetHighUTXOAndSum") - ret0, _ := ret[0].(*btcjson.ListUnspentResult) - ret1, _ := ret[1].(float64) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetHighUTXOAndSum indicates an expected call of GetHighUTXOAndSum. -func (mr *MockBTCWalletMockRecorder) GetHighUTXOAndSum() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHighUTXOAndSum", reflect.TypeOf((*MockBTCWallet)(nil).GetHighUTXOAndSum)) -} - -// GetNetParams mocks base method. -func (m *MockBTCWallet) GetNetParams() *chaincfg.Params { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNetParams") - ret0, _ := ret[0].(*chaincfg.Params) - return ret0 -} - -// GetNetParams indicates an expected call of GetNetParams. -func (mr *MockBTCWalletMockRecorder) GetNetParams() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetParams", reflect.TypeOf((*MockBTCWallet)(nil).GetNetParams)) -} - -// GetRawChangeAddress mocks base method. -func (m *MockBTCWallet) GetRawChangeAddress(account string) (btcutil.Address, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRawChangeAddress", account) - ret0, _ := ret[0].(btcutil.Address) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetRawChangeAddress indicates an expected call of GetRawChangeAddress. -func (mr *MockBTCWalletMockRecorder) GetRawChangeAddress(account interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRawChangeAddress", reflect.TypeOf((*MockBTCWallet)(nil).GetRawChangeAddress), account) -} - -// GetRawTransaction mocks base method. -func (m *MockBTCWallet) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRawTransaction", txHash) - ret0, _ := ret[0].(*btcutil.Tx) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetRawTransaction indicates an expected call of GetRawTransaction. -func (mr *MockBTCWalletMockRecorder) GetRawTransaction(txHash interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRawTransaction", reflect.TypeOf((*MockBTCWallet)(nil).GetRawTransaction), txHash) -} - -// GetWalletLockTime mocks base method. -func (m *MockBTCWallet) GetWalletLockTime() int64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetWalletLockTime") - ret0, _ := ret[0].(int64) - return ret0 -} - -// GetWalletLockTime indicates an expected call of GetWalletLockTime. -func (mr *MockBTCWalletMockRecorder) GetWalletLockTime() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWalletLockTime", reflect.TypeOf((*MockBTCWallet)(nil).GetWalletLockTime)) -} - -// GetWalletPass mocks base method. -func (m *MockBTCWallet) GetWalletPass() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetWalletPass") - ret0, _ := ret[0].(string) - return ret0 -} - -// GetWalletPass indicates an expected call of GetWalletPass. -func (mr *MockBTCWalletMockRecorder) GetWalletPass() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWalletPass", reflect.TypeOf((*MockBTCWallet)(nil).GetWalletPass)) -} - -// ListReceivedByAddress mocks base method. -func (m *MockBTCWallet) ListReceivedByAddress() ([]btcjson.ListReceivedByAddressResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListReceivedByAddress") - ret0, _ := ret[0].([]btcjson.ListReceivedByAddressResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListReceivedByAddress indicates an expected call of ListReceivedByAddress. -func (mr *MockBTCWalletMockRecorder) ListReceivedByAddress() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListReceivedByAddress", reflect.TypeOf((*MockBTCWallet)(nil).ListReceivedByAddress)) -} - -// ListUnspent mocks base method. -func (m *MockBTCWallet) ListUnspent() ([]btcjson.ListUnspentResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListUnspent") - ret0, _ := ret[0].([]btcjson.ListUnspentResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListUnspent indicates an expected call of ListUnspent. -func (mr *MockBTCWalletMockRecorder) ListUnspent() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUnspent", reflect.TypeOf((*MockBTCWallet)(nil).ListUnspent)) -} - -// SendRawTransaction mocks base method. -func (m *MockBTCWallet) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendRawTransaction", tx, allowHighFees) - ret0, _ := ret[0].(*chainhash.Hash) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SendRawTransaction indicates an expected call of SendRawTransaction. -func (mr *MockBTCWalletMockRecorder) SendRawTransaction(tx, allowHighFees interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRawTransaction", reflect.TypeOf((*MockBTCWallet)(nil).SendRawTransaction), tx, allowHighFees) -} - -// SignRawTransactionWithWallet mocks base method. -func (m *MockBTCWallet) SignRawTransactionWithWallet(tx *wire.MsgTx) (*wire.MsgTx, bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SignRawTransactionWithWallet", tx) - ret0, _ := ret[0].(*wire.MsgTx) - ret1, _ := ret[1].(bool) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// SignRawTransactionWithWallet indicates an expected call of SignRawTransactionWithWallet. -func (mr *MockBTCWalletMockRecorder) SignRawTransactionWithWallet(tx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignRawTransactionWithWallet", reflect.TypeOf((*MockBTCWallet)(nil).SignRawTransactionWithWallet), tx) -} - -// Stop mocks base method. -func (m *MockBTCWallet) Stop() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Stop") -} - -// Stop indicates an expected call of Stop. -func (mr *MockBTCWalletMockRecorder) Stop() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockBTCWallet)(nil).Stop)) -} - -// TxDetails mocks base method. -func (m *MockBTCWallet) TxDetails(txHash *chainhash.Hash, pkScript []byte) (*chainntnfs.TxConfirmation, btcclient.TxStatus, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TxDetails", txHash, pkScript) - ret0, _ := ret[0].(*chainntnfs.TxConfirmation) - ret1, _ := ret[1].(btcclient.TxStatus) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// TxDetails indicates an expected call of TxDetails. -func (mr *MockBTCWalletMockRecorder) TxDetails(txHash, pkScript interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TxDetails", reflect.TypeOf((*MockBTCWallet)(nil).TxDetails), txHash, pkScript) -} - -// WalletPassphrase mocks base method. -func (m *MockBTCWallet) WalletPassphrase(passphrase string, timeoutSecs int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WalletPassphrase", passphrase, timeoutSecs) - ret0, _ := ret[0].(error) - return ret0 -} - -// WalletPassphrase indicates an expected call of WalletPassphrase. -func (mr *MockBTCWalletMockRecorder) WalletPassphrase(passphrase, timeoutSecs interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WalletPassphrase", reflect.TypeOf((*MockBTCWallet)(nil).WalletPassphrase), passphrase, timeoutSecs) -} diff --git a/testutil/port.go b/testutil/port.go deleted file mode 100644 index 19ad7f7..0000000 --- a/testutil/port.go +++ /dev/null @@ -1,61 +0,0 @@ -package testutil - -import ( - "fmt" - mrand "math/rand/v2" - "net" - "sync" - "testing" -) - -// Track allocated ports, protected by a mutex -var ( - allocatedPorts = make(map[int]struct{}) - portMutex sync.Mutex -) - -// AllocateUniquePort tries to find an available TCP port on the localhost -// by testing multiple random ports within a specified range. -func AllocateUniquePort(t *testing.T) int { - randPort := func(base, spread int) int { - return base + mrand.IntN(spread) - } - - // Base port and spread range for port selection - const ( - basePort = 20000 - portRange = 30000 - ) - - // Try up to 10 times to find an available port - for i := 0; i < 10; i++ { - port := randPort(basePort, portRange) - - // Lock the mutex to check and modify the shared map - portMutex.Lock() - if _, exists := allocatedPorts[port]; exists { - // Port already allocated, try another one - portMutex.Unlock() - continue - } - - listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) - if err != nil { - portMutex.Unlock() - continue - } - - allocatedPorts[port] = struct{}{} - portMutex.Unlock() - - if err := listener.Close(); err != nil { - continue - } - - return port - } - - // If no available port was found, fail the test - t.Fatalf("failed to find an available port in range %d-%d", basePort, basePort+portRange) - return 0 -} diff --git a/testutil/store.go b/testutil/store.go deleted file mode 100644 index 63d66dc..0000000 --- a/testutil/store.go +++ /dev/null @@ -1,26 +0,0 @@ -package testutil - -import ( - "github.com/babylonlabs-io/vigilante/config" - "github.com/lightningnetwork/lnd/kvdb" - "github.com/stretchr/testify/require" - "testing" -) - -func MakeTestBackend(t *testing.T) kvdb.Backend { - tempDirName := t.TempDir() - - cfg := config.DefaultDBConfig() - - cfg.DBPath = tempDirName - - backend, err := cfg.GetDBBackend() - require.NoError(t, err) - - t.Cleanup(func() { - err := backend.Close() - require.NoError(t, err) - }) - - return backend -} diff --git a/testutil/version.go b/testutil/version.go deleted file mode 100644 index 5a2dfaf..0000000 --- a/testutil/version.go +++ /dev/null @@ -1,32 +0,0 @@ -package testutil - -import ( - "fmt" - "golang.org/x/mod/modfile" - "os" - "path/filepath" -) - -// GetBabylonVersion returns babylond version from go.mod -func GetBabylonVersion() (string, error) { - goModPath := filepath.Join("..", "go.mod") - data, err := os.ReadFile(goModPath) - if err != nil { - return "", err - } - - // Parse the go.mod file - modFile, err := modfile.Parse("go.mod", data, nil) - if err != nil { - return "", err - } - - const modName = "github.com/babylonlabs-io/babylon" - for _, require := range modFile.Require { - if require.Mod.Path == modName { - return require.Mod.Version, nil - } - } - - return "", fmt.Errorf("module %s not found", modName) -} From b02d7bd5cee680fa63b9e04c61a1550bc1c1dbdf Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 16:57:57 +0530 Subject: [PATCH 08/32] fix --- e2etest/test_manager.go | 2 +- go.mod | 35 ++++++++++++++++++++++- go.sum | 33 ++++++++++++++++++++-- testutil/port.go | 61 +++++++++++++++++++++++++++++++++++++++++ testutil/version.go | 32 +++++++++++++++++++++ 5 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 testutil/port.go create mode 100644 testutil/version.go diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 7e11907..d9de253 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -15,12 +15,12 @@ import ( bbnclient "github.com/babylonlabs-io/babylon/client/client" bbncfg "github.com/babylonlabs-io/babylon/client/config" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" "github.com/stretchr/testify/require" ) diff --git a/go.mod b/go.mod index e713e71..0165b3f 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,10 @@ require ( github.com/cosmos/gogoproto v1.7.0 github.com/go-chi/chi/v5 v5.1.0 github.com/lightningnetwork/lnd v0.17.0-beta + github.com/ory/dockertest/v3 v3.10.0 github.com/spf13/viper v1.19.0 go.uber.org/zap v1.27.0 + golang.org/x/mod v0.17.0 ) require ( @@ -26,6 +28,7 @@ require ( cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect cosmossdk.io/api v0.7.5 // indirect + cosmossdk.io/client/v2 v2.0.0-beta.1 // indirect cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/core v0.11.1 // indirect cosmossdk.io/depinject v1.0.0 // indirect @@ -33,14 +36,23 @@ require ( cosmossdk.io/log v1.4.1 // indirect cosmossdk.io/math v1.4.0 // indirect cosmossdk.io/store v1.1.0 // 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/nft v0.1.1 // indirect cosmossdk.io/x/tx v0.13.4 // indirect cosmossdk.io/x/upgrade v0.1.4 // indirect + dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/CosmWasm/wasmd v0.53.0 // indirect + github.com/CosmWasm/wasmvm/v2 v2.1.3 // indirect github.com/DataDog/datadog-go v3.2.0+incompatible // indirect github.com/DataDog/zstd v1.5.5 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/aead/siphash v1.0.1 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/aws/aws-sdk-go v1.44.312 // indirect @@ -58,6 +70,7 @@ require ( github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -67,6 +80,7 @@ require ( github.com/cometbft/cometbft-db v0.15.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/containerd/continuity v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect @@ -75,7 +89,9 @@ require ( github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v1.2.0 // indirect + github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd // indirect github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect + github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead // indirect github.com/cosmos/ibc-go/v8 v8.4.0 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect @@ -89,6 +105,11 @@ require ( github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v4 v4.3.0 // indirect github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/cli v25.0.6+incompatible // indirect + github.com/docker/docker v25.0.6+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect @@ -115,8 +136,10 @@ require ( github.com/google/btree v1.1.3 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/s2a-go v0.1.7 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.3 // indirect @@ -154,11 +177,14 @@ require ( github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/pgx/v4 v4.18.1 // indirect github.com/jessevdk/go-flags v1.4.0 // indirect + github.com/jinzhu/copier v0.3.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/jrick/logrotate v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/jsternberg/zap-logfmt v1.3.0 // indirect + github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kkdai/bstream v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -184,6 +210,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect @@ -192,6 +219,9 @@ require ( github.com/nwaples/rardecode v1.1.2 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect github.com/oklog/run v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/runc v1.1.12 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pierrec/lz4/v4 v4.1.8 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -205,6 +235,7 @@ require ( github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/cors v1.11.1 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/shamaton/msgpack/v2 v2.2.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/strangelove-ventures/cometbft-client v0.1.0 // indirect @@ -219,6 +250,9 @@ require ( github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect @@ -243,7 +277,6 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 // indirect go.opentelemetry.io/proto/otlp v0.9.0 // indirect golang.org/x/crypto v0.28.0 // indirect - golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/go.sum b/go.sum index d1ef58a..971e58d 100644 --- a/go.sum +++ b/go.sum @@ -212,6 +212,8 @@ cosmossdk.io/x/tx v0.13.4 h1:Eg0PbJgeO0gM8p5wx6xa0fKR7hIV6+8lC56UrsvSo0Y= cosmossdk.io/x/tx v0.13.4/go.mod h1:BkFqrnGGgW50Y6cwTy+JvgAhiffbGEKW6KF9ufcDpvk= cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= @@ -461,6 +463,8 @@ github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBS github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -487,6 +491,10 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v25.0.6+incompatible h1:F1mCw1kUGixOkM8WQbcG5kniPvP8XCFxreFxl4b/UnY= +github.com/docker/cli v25.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= +github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -587,12 +595,16 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -716,6 +728,8 @@ github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8q github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 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= @@ -914,7 +928,11 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr 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/jsternberg/zap-logfmt v1.3.0 h1:z1n1AOHVVydOOVuyphbOKyR4NICDQFiJMn1IK5hVQ5Y= +github.com/jsternberg/zap-logfmt v1.3.0/go.mod h1:N3DENp9WNmCZxvkBD/eReWwz1149BK6jEN9cQ4fNwZE= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b h1:FQ7+9fxhyp82ks9vAuyPzG0/vVbWwMwLJ+P6yJI5FN8= +github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b/go.mod h1:HMcgvsgd0Fjj4XXDkbjdmlbI505rUPBs6WBMYg2pXks= 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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -1046,6 +1064,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1119,6 +1139,8 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh 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/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= +github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= @@ -1316,6 +1338,13 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= diff --git a/testutil/port.go b/testutil/port.go new file mode 100644 index 0000000..19ad7f7 --- /dev/null +++ b/testutil/port.go @@ -0,0 +1,61 @@ +package testutil + +import ( + "fmt" + mrand "math/rand/v2" + "net" + "sync" + "testing" +) + +// Track allocated ports, protected by a mutex +var ( + allocatedPorts = make(map[int]struct{}) + portMutex sync.Mutex +) + +// AllocateUniquePort tries to find an available TCP port on the localhost +// by testing multiple random ports within a specified range. +func AllocateUniquePort(t *testing.T) int { + randPort := func(base, spread int) int { + return base + mrand.IntN(spread) + } + + // Base port and spread range for port selection + const ( + basePort = 20000 + portRange = 30000 + ) + + // Try up to 10 times to find an available port + for i := 0; i < 10; i++ { + port := randPort(basePort, portRange) + + // Lock the mutex to check and modify the shared map + portMutex.Lock() + if _, exists := allocatedPorts[port]; exists { + // Port already allocated, try another one + portMutex.Unlock() + continue + } + + listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + portMutex.Unlock() + continue + } + + allocatedPorts[port] = struct{}{} + portMutex.Unlock() + + if err := listener.Close(); err != nil { + continue + } + + return port + } + + // If no available port was found, fail the test + t.Fatalf("failed to find an available port in range %d-%d", basePort, basePort+portRange) + return 0 +} diff --git a/testutil/version.go b/testutil/version.go new file mode 100644 index 0000000..5a2dfaf --- /dev/null +++ b/testutil/version.go @@ -0,0 +1,32 @@ +package testutil + +import ( + "fmt" + "golang.org/x/mod/modfile" + "os" + "path/filepath" +) + +// GetBabylonVersion returns babylond version from go.mod +func GetBabylonVersion() (string, error) { + goModPath := filepath.Join("..", "go.mod") + data, err := os.ReadFile(goModPath) + if err != nil { + return "", err + } + + // Parse the go.mod file + modFile, err := modfile.Parse("go.mod", data, nil) + if err != nil { + return "", err + } + + const modName = "github.com/babylonlabs-io/babylon" + for _, require := range modFile.Require { + if require.Mod.Path == modName { + return require.Mod.Version, nil + } + } + + return "", fmt.Errorf("module %s not found", modName) +} From 42d1f3072679a12aa660a8f2f000b3783acb1205 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 17:55:14 +0530 Subject: [PATCH 09/32] fix --- e2etest/e2e_test.go | 51 ++++++++++++++++++++++++++++++++++ e2etest/queue_setup.go | 61 +++++++++++++++++++++++++++++++++++++++++ e2etest/test_manager.go | 12 ++++---- 3 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 e2etest/e2e_test.go create mode 100644 e2etest/queue_setup.go diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go new file mode 100644 index 0000000..f7cbe41 --- /dev/null +++ b/e2etest/e2e_test.go @@ -0,0 +1,51 @@ +package e2etest + +import ( + "encoding/hex" + "encoding/json" + "math/rand" + "testing" + "time" + + bbndatagen "github.com/babylonlabs-io/babylon/testutil/datagen" + queuecli "github.com/babylonlabs-io/staking-queue-client/client" + "github.com/babylonlabs-io/staking-queue-client/config" + "github.com/stretchr/testify/require" +) + +func TestQueueConsumer(t *testing.T) { + // create event consumer + queueCfg := config.DefaultQueueConfig() + queueConsumer, err := setupTestQueueConsumer(t, queueCfg) + require.NoError(t, err) + stakingChan, err := queueConsumer.ActiveStakingQueue.ReceiveMessages() + require.NoError(t, err) + + defer queueConsumer.Stop() + + n := 1 + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + stakingEventList := make([]*queuecli.StakingEvent, 0) + for i := 0; i < n; i++ { + stakingEvent := queuecli.NewActiveStakingEvent( + hex.EncodeToString(bbndatagen.GenRandomByteArray(r, 10)), + hex.EncodeToString(bbndatagen.GenRandomByteArray(r, 10)), + []string{hex.EncodeToString(bbndatagen.GenRandomByteArray(r, 10))}, + 1000, + ) + err = queueConsumer.PushActiveStakingEvent(&stakingEvent) + require.NoError(t, err) + stakingEventList = append(stakingEventList, &stakingEvent) + } + + for i := 0; i < n; i++ { + stakingEventBytes := <-stakingChan + var receivedStakingEvent queuecli.StakingEvent + err = json.Unmarshal([]byte(stakingEventBytes.Body), &receivedStakingEvent) + require.NoError(t, err) + require.Equal(t, stakingEventList[i].StakingTxHashHex, receivedStakingEvent.StakingTxHashHex) + err = queueConsumer.ActiveStakingQueue.DeleteMessage(stakingEventBytes.Receipt) + require.NoError(t, err) + } +} diff --git a/e2etest/queue_setup.go b/e2etest/queue_setup.go new file mode 100644 index 0000000..1f62e80 --- /dev/null +++ b/e2etest/queue_setup.go @@ -0,0 +1,61 @@ +package e2etest + +import ( + "fmt" + "strings" + "testing" + + "github.com/babylonlabs-io/staking-queue-client/client" + "github.com/babylonlabs-io/staking-queue-client/queuemngr" + "github.com/rabbitmq/amqp091-go" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/babylonlabs-io/staking-queue-client/config" +) + +func setupTestQueueConsumer(t *testing.T, cfg *config.QueueConfig) (*queuemngr.QueueManager, error) { + amqpURI := fmt.Sprintf("amqp://%s:%s@%s", cfg.QueueUser, cfg.QueuePassword, cfg.Url) + conn, err := amqp091.Dial(amqpURI) + if err != nil { + t.Fatalf("failed to connect to RabbitMQ in test: %v", err) + } + defer conn.Close() + err = purgeQueues(conn, []string{ + client.ActiveStakingQueueName, + }) + if err != nil { + return nil, err + } + + // Start the actual queue processing in our codebase + + err = cfg.Validate() + require.NoError(t, err) + queues, err := queuemngr.NewQueueManager(cfg, zap.NewNop()) + require.NoError(t, err) + + return queues, nil +} + +// purgeQueues purges all messages from the given list of queues. +func purgeQueues(conn *amqp091.Connection, queues []string) error { + ch, err := conn.Channel() + if err != nil { + return fmt.Errorf("failed to open a channel in test: %w", err) + } + defer ch.Close() + + for _, queue := range queues { + _, err := ch.QueuePurge(queue, false) + if err != nil { + if strings.Contains(err.Error(), "no queue") { + fmt.Printf("Queue '%s' not found, ignoring...\n", queue) + continue // Ignore this error and proceed with the next queue + } + return fmt.Errorf("failed to purge queue in test %s: %w", queue, err) + } + } + + return nil +} diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index d9de253..756da3b 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -25,14 +25,14 @@ import ( ) var ( - submitterAddrStr = "bbn1eppc73j56382wjn6nnq3quu5eye4pmm087xfdh" //nolint:unused - babylonTag = []byte{1, 2, 3, 4} //nolint:unused - babylonTagHex = hex.EncodeToString(babylonTag) //nolint:unused + // submitterAddrStr = "bbn1eppc73j56382wjn6nnq3quu5eye4pmm087xfdh" //nolint:unused + // babylonTag = []byte{1, 2, 3, 4} //nolint:unused + // babylonTagHex = hex.EncodeToString(babylonTag) //nolint:unused eventuallyWaitTimeOut = 40 * time.Second eventuallyPollTime = 1 * time.Second regtestParams = &chaincfg.RegressionNetParams - defaultEpochInterval = uint(400) //nolint:unused + // defaultEpochInterval = uint(400) //nolint:unused ) type TestManager struct { @@ -105,9 +105,7 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u // wait until Babylon is ready require.Eventually(t, func() bool { _, err := babylonClient.CurrentEpoch() - if err != nil { - return false - } + require.NoError(t, err) //log.Infof("Babylon is ready: %v", resp) return true }, eventuallyWaitTimeOut, eventuallyPollTime) From f6b2886f3e106777dd9d54d1db93820a318b2a4e Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 8 Dec 2024 22:37:22 +0530 Subject: [PATCH 10/32] fix --- e2etest/test_manager.go | 52 ++ e2etest/test_manager_btcstaking.go | 898 +++++++++++++++++++++++++++++ internal/types/utxo.go | 51 ++ 3 files changed, 1001 insertions(+) create mode 100644 e2etest/test_manager_btcstaking.go create mode 100644 internal/types/utxo.go diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 756da3b..a8c953b 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -2,6 +2,7 @@ package e2etest import ( "bytes" + "context" "encoding/hex" "fmt" "os" @@ -14,6 +15,8 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/config" bbnclient "github.com/babylonlabs-io/babylon/client/client" bbncfg "github.com/babylonlabs-io/babylon/client/config" + bbn "github.com/babylonlabs-io/babylon/types" + btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" @@ -21,6 +24,7 @@ import ( "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + pv "github.com/cosmos/relayer/v2/relayer/provider" "github.com/stretchr/testify/require" ) @@ -188,3 +192,51 @@ func DefaultStakingIndexerConfig() *config.Config { return defaultConfig } + +// RetrieveTransactionFromMempool fetches transactions from the mempool for the given hashes +func (tm *TestManager) RetrieveTransactionFromMempool(t *testing.T, hashes []*chainhash.Hash) []*btcutil.Tx { + var txs []*btcutil.Tx + for _, txHash := range hashes { + tx, err := tm.WalletClient.GetRawTransaction(txHash) + require.NoError(t, err) + txs = append(txs, tx) + } + + return txs +} + +func (tm *TestManager) CatchUpBTCLightClient(t *testing.T) { + btcHeight, err := tm.WalletClient.GetBlockCount() + require.NoError(t, err) + + tipResp, err := tm.BabylonClient.BTCHeaderChainTip() + require.NoError(t, err) + btclcHeight := tipResp.Header.Height + + var headers []*wire.BlockHeader + for i := int(btclcHeight + 1); i <= int(btcHeight); i++ { + hash, err := tm.WalletClient.GetBlockHash(int64(i)) + require.NoError(t, err) + header, err := tm.WalletClient.GetBlockHeader(hash) + require.NoError(t, err) + headers = append(headers, header) + } + + _, err = tm.InsertBTCHeadersToBabylon(headers) + require.NoError(t, err) +} + +func (tm *TestManager) InsertBTCHeadersToBabylon(headers []*wire.BlockHeader) (*pv.RelayerTxResponse, error) { + var headersBytes []bbn.BTCHeaderBytes + + for _, h := range headers { + headersBytes = append(headersBytes, bbn.NewBTCHeaderBytesFromBlockHeader(h)) + } + + msg := btclctypes.MsgInsertHeaders{ + Headers: headersBytes, + Signer: tm.MustGetBabylonSigner(), + } + + return tm.BabylonClient.InsertHeaders(context.Background(), &msg) +} diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go new file mode 100644 index 0000000..ee0d276 --- /dev/null +++ b/e2etest/test_manager_btcstaking.go @@ -0,0 +1,898 @@ +package e2etest + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "math/rand" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/avast/retry-go/v4" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" + "github.com/babylonlabs-io/babylon/btcstaking" + txformat "github.com/babylonlabs-io/babylon/btctxformatter" + "github.com/babylonlabs-io/babylon/crypto/eots" + asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" + "github.com/babylonlabs-io/babylon/testutil/datagen" + bbn "github.com/babylonlabs-io/babylon/types" + btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" + ftypes "github.com/babylonlabs-io/babylon/x/finality/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkquery "github.com/cosmos/cosmos-sdk/types/query" + sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/relayer/v2/relayer/provider" + "github.com/stretchr/testify/require" +) + +var ( + r = rand.New(rand.NewSource(time.Now().Unix())) + + // covenant + covenantSk, _ = btcec.PrivKeyFromBytes( + []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + ) +) + +func (tm *TestManager) getBTCUnbondingTime(t *testing.T) uint32 { + bsParams, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + + return bsParams.Params.UnbondingTimeBlocks +} + +func (tm *TestManager) CreateFinalityProvider(t *testing.T) (*bstypes.FinalityProvider, *btcec.PrivateKey) { + var err error + signerAddr := tm.BabylonClient.MustGetAddr() + addr := sdk.MustAccAddressFromBech32(signerAddr) + + fpSK, _, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + btcFp, err := datagen.GenRandomFinalityProviderWithBTCBabylonSKs(r, fpSK, addr) + require.NoError(t, err) + + /* + create finality provider + */ + commission := sdkmath.LegacyZeroDec() + msgNewVal := &bstypes.MsgCreateFinalityProvider{ + Addr: signerAddr, + Description: &stakingtypes.Description{Moniker: datagen.GenRandomHexStr(r, 10)}, + Commission: &commission, + BtcPk: btcFp.BtcPk, + Pop: btcFp.Pop, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgNewVal, nil, nil) + require.NoError(t, err) + + return btcFp, fpSK +} + +func (tm *TestManager) CreateBTCDelegation( + t *testing.T, + fpSK *btcec.PrivateKey, +) (*datagen.TestStakingSlashingInfo, *datagen.TestUnbondingSlashingInfo, *btcec.PrivateKey) { + signerAddr := tm.BabylonClient.MustGetAddr() + addr := sdk.MustAccAddressFromBech32(signerAddr) + + fpPK := fpSK.PubKey() + + /* + create BTC delegation + */ + // generate staking tx and slashing tx + bsParams, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + covenantBtcPks, err := bbnPksToBtcPks(bsParams.Params.CovenantPks) + require.NoError(t, err) + stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks + // get top UTXO + topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() + require.NoError(t, err) + topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) + require.NoError(t, err) + // staking value + stakingValue := int64(topUTXO.Amount) / 3 + + // generate legitimate BTC del + stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash := tm.createStakingAndSlashingTx(t, fpSK, bsParams, covenantBtcPks, topUTXO, stakingValue, stakingTimeBlocks) + + // send staking tx to Bitcoin node's mempool + _, err = tm.WalletClient.SendRawTransaction(stakingMsgTx, true) + require.NoError(t, err) + + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{stakingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineBlock(t) + require.Equal(t, 2, len(mBlock.Transactions)) + + // wait until staking tx is on Bitcoin + require.Eventually(t, func() bool { + _, err := tm.WalletClient.GetRawTransaction(stakingMsgTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + // get spv proof of the BTC staking tx + stakingTxInfo := getTxInfo(t, mBlock) + + // insert k empty blocks to Bitcoin + btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() + require.NoError(t, err) + btccParams := btccParamsResp.Params + for i := 0; i < int(btccParams.BtcConfirmationDepth); i++ { + tm.mineBlock(t) + } + + stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) + require.NoError(t, err) + + // create PoP + pop, err := bstypes.NewPoPBTC(addr, tm.WalletPrivKey) + require.NoError(t, err) + slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + // generate proper delegator sig + require.NoError(t, err) + + delegatorSig, err := stakingSlashingInfo.SlashingTx.Sign( + stakingMsgTx, + stakingOutIdx, + slashingSpendPath.GetPkScriptPath(), + tm.WalletPrivKey, + ) + require.NoError(t, err) + + // Generate all data necessary for unbonding + unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( + t, + fpPK, + bsParams, + covenantBtcPks, + stakingSlashingInfo, + stakingMsgTxHash, + stakingOutIdx, + stakingTimeBlocks, + ) + + tm.CatchUpBTCLightClient(t) + + // Build a message to send + // submit BTC delegation to Babylon + msgBTCDel := &bstypes.MsgCreateBTCDelegation{ + StakerAddr: signerAddr, + Pop: pop, + BtcPk: bbn.NewBIP340PubKeyFromBTCPK(tm.WalletPrivKey.PubKey()), + FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, + StakingTime: stakingTimeBlocks, + StakingValue: stakingValue, + StakingTx: stakingTxInfo.Transaction, + StakingTxInclusionProof: &bstypes.InclusionProof{ + Key: stakingTxInfo.Key, + Proof: stakingTxInfo.Proof, + }, + SlashingTx: stakingSlashingInfo.SlashingTx, + DelegatorSlashingSig: delegatorSig, + // Unbonding related data + UnbondingTime: tm.getBTCUnbondingTime(t), + UnbondingTx: unbondingTxBytes, + UnbondingValue: unbondingSlashingInfo.UnbondingInfo.UnbondingOutput.Value, + UnbondingSlashingTx: unbondingSlashingInfo.SlashingTx, + DelegatorUnbondingSlashingSig: slashingTxSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgBTCDel, nil, nil) + require.NoError(t, err) + t.Logf("submitted MsgCreateBTCDelegation") + + // generate and insert new covenant signature, to activate the BTC delegation + tm.addCovenantSig( + t, + signerAddr, + stakingMsgTx, + stakingMsgTxHash, + fpSK, slashingSpendPath, + stakingSlashingInfo, + unbondingSlashingInfo, + unbondingSlashingPathSpendInfo, + stakingOutIdx, + ) + + return stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey +} + +func (tm *TestManager) CreateBTCDelegationWithoutIncl( + t *testing.T, + fpSK *btcec.PrivateKey, +) (*wire.MsgTx, *datagen.TestStakingSlashingInfo, *datagen.TestUnbondingSlashingInfo, *btcec.PrivateKey) { + signerAddr := tm.BabylonClient.MustGetAddr() + addr := sdk.MustAccAddressFromBech32(signerAddr) + + fpPK := fpSK.PubKey() + + /* + create BTC delegation + */ + // generate staking tx and slashing tx + bsParams, err := tm.BabylonClient.BTCStakingParams() + require.NoError(t, err) + covenantBtcPks, err := bbnPksToBtcPks(bsParams.Params.CovenantPks) + require.NoError(t, err) + stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks + // get top UTXO + topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() + require.NoError(t, err) + topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) + require.NoError(t, err) + // staking value + stakingValue := int64(topUTXO.Amount) / 3 + + // generate legitimate BTC del + stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash := tm.createStakingAndSlashingTx(t, fpSK, bsParams, covenantBtcPks, topUTXO, stakingValue, stakingTimeBlocks) + + stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) + require.NoError(t, err) + + // create PoP + pop, err := bstypes.NewPoPBTC(addr, tm.WalletPrivKey) + require.NoError(t, err) + slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + // generate proper delegator sig + require.NoError(t, err) + + delegatorSig, err := stakingSlashingInfo.SlashingTx.Sign( + stakingMsgTx, + stakingOutIdx, + slashingSpendPath.GetPkScriptPath(), + tm.WalletPrivKey, + ) + require.NoError(t, err) + + // Generate all data necessary for unbonding + unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( + t, + fpPK, + bsParams, + covenantBtcPks, + stakingSlashingInfo, + stakingMsgTxHash, + stakingOutIdx, + stakingTimeBlocks, + ) + + var stakingTxBuf bytes.Buffer + err = stakingMsgTx.Serialize(&stakingTxBuf) + require.NoError(t, err) + + // submit BTC delegation to Babylon + msgBTCDel := &bstypes.MsgCreateBTCDelegation{ + StakerAddr: signerAddr, + Pop: pop, + BtcPk: bbn.NewBIP340PubKeyFromBTCPK(tm.WalletPrivKey.PubKey()), + FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, + StakingTime: stakingTimeBlocks, + StakingValue: stakingValue, + StakingTx: stakingTxBuf.Bytes(), + StakingTxInclusionProof: nil, + SlashingTx: stakingSlashingInfo.SlashingTx, + DelegatorSlashingSig: delegatorSig, + // Unbonding related data + UnbondingTime: uint32(tm.getBTCUnbondingTime(t)), + UnbondingTx: unbondingTxBytes, + UnbondingValue: unbondingSlashingInfo.UnbondingInfo.UnbondingOutput.Value, + UnbondingSlashingTx: unbondingSlashingInfo.SlashingTx, + DelegatorUnbondingSlashingSig: slashingTxSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgBTCDel, nil, nil) + require.NoError(t, err) + t.Logf("submitted MsgCreateBTCDelegation") + + // generate and insert new covenant signature, to activate the BTC delegation + tm.addCovenantSig( + t, + signerAddr, + stakingMsgTx, + stakingMsgTxHash, + fpSK, slashingSpendPath, + stakingSlashingInfo, + unbondingSlashingInfo, + unbondingSlashingPathSpendInfo, + stakingOutIdx, + ) + + return stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey +} + +func (tm *TestManager) createStakingAndSlashingTx( + t *testing.T, fpSK *btcec.PrivateKey, + bsParams *bstypes.QueryParamsResponse, + covenantBtcPks []*btcec.PublicKey, + topUTXO *types.UTXO, + stakingValue int64, + stakingTimeBlocks uint32, +) (*wire.MsgTx, *datagen.TestStakingSlashingInfo, *chainhash.Hash) { + // generate staking tx and slashing tx + fpPK := fpSK.PubKey() + + // generate legitimate BTC del + stakingSlashingInfo := datagen.GenBTCStakingSlashingInfoWithOutPoint( + r, + t, + regtestParams, + topUTXO.GetOutPoint(), + tm.WalletPrivKey, + []*btcec.PublicKey{fpPK}, + covenantBtcPks, + bsParams.Params.CovenantQuorum, + uint16(stakingTimeBlocks), + stakingValue, + bsParams.Params.SlashingPkScript, + bsParams.Params.SlashingRate, + uint16(tm.getBTCUnbondingTime(t)), + ) + // sign staking tx and overwrite the staking tx to the signed version + // NOTE: the tx hash has changed here since stakingMsgTx is pre-segwit + stakingMsgTx, signed, err := tm.WalletClient.SignRawTransactionWithWallet(stakingSlashingInfo.StakingTx) + require.NoError(t, err) + require.True(t, signed) + // overwrite staking tx + stakingSlashingInfo.StakingTx = stakingMsgTx + // get signed staking tx hash + stakingMsgTxHash1 := stakingSlashingInfo.StakingTx.TxHash() + stakingMsgTxHash := &stakingMsgTxHash1 + t.Logf("signed staking tx hash: %s", stakingMsgTxHash.String()) + + // change outpoint tx hash of slashing tx to the txhash of the signed staking tx + slashingMsgTx, err := stakingSlashingInfo.SlashingTx.ToMsgTx() + require.NoError(t, err) + slashingMsgTx.TxIn[0].PreviousOutPoint.Hash = stakingSlashingInfo.StakingTx.TxHash() + // update slashing tx + stakingSlashingInfo.SlashingTx, err = bstypes.NewBTCSlashingTxFromMsgTx(slashingMsgTx) + require.NoError(t, err) + + return stakingMsgTx, stakingSlashingInfo, stakingMsgTxHash +} + +func (tm *TestManager) createUnbondingData( + t *testing.T, + fpPK *btcec.PublicKey, + bsParams *bstypes.QueryParamsResponse, + covenantBtcPks []*btcec.PublicKey, + stakingSlashingInfo *datagen.TestStakingSlashingInfo, + stakingMsgTxHash *chainhash.Hash, + stakingOutIdx uint32, + stakingTimeBlocks uint32, +) (*datagen.TestUnbondingSlashingInfo, *btcstaking.SpendInfo, []byte, *bbn.BIP340Signature) { + fee := int64(1000) + unbondingValue := stakingSlashingInfo.StakingInfo.StakingOutput.Value - fee + unbondingSlashingInfo := datagen.GenBTCUnbondingSlashingInfo( + r, + t, + regtestParams, + tm.WalletPrivKey, + []*btcec.PublicKey{fpPK}, + covenantBtcPks, + bsParams.Params.CovenantQuorum, + wire.NewOutPoint(stakingMsgTxHash, stakingOutIdx), + uint16(stakingTimeBlocks), + unbondingValue, + bsParams.Params.SlashingPkScript, + bsParams.Params.SlashingRate, + uint16(tm.getBTCUnbondingTime(t)), + ) + unbondingTxBytes, err := bbn.SerializeBTCTx(unbondingSlashingInfo.UnbondingTx) + require.NoError(t, err) + + unbondingSlashingPathSpendInfo, err := unbondingSlashingInfo.UnbondingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + slashingTxSig, err := unbondingSlashingInfo.SlashingTx.Sign( + unbondingSlashingInfo.UnbondingTx, + 0, // Only one output in the unbonding tx + unbondingSlashingPathSpendInfo.GetPkScriptPath(), + tm.WalletPrivKey, + ) + require.NoError(t, err) + + return unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig +} + +func (tm *TestManager) addCovenantSig( + t *testing.T, + signerAddr string, + stakingMsgTx *wire.MsgTx, + stakingMsgTxHash *chainhash.Hash, + fpSK *btcec.PrivateKey, + slashingSpendPath *btcstaking.SpendInfo, + stakingSlashingInfo *datagen.TestStakingSlashingInfo, + unbondingSlashingInfo *datagen.TestUnbondingSlashingInfo, + unbondingSlashingPathSpendInfo *btcstaking.SpendInfo, + stakingOutIdx uint32, +) { + // TODO: Make this handle multiple covenant signatures + fpEncKey, err := asig.NewEncryptionKeyFromBTCPK(fpSK.PubKey()) + require.NoError(t, err) + covenantSig, err := stakingSlashingInfo.SlashingTx.EncSign( + stakingMsgTx, + stakingOutIdx, + slashingSpendPath.GetPkScriptPath(), + covenantSk, + fpEncKey, + ) + require.NoError(t, err) + // TODO: Add covenant sigs for all covenants + // add covenant sigs + // covenant Schnorr sig on unbonding tx + unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + unbondingTxCovenantSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingSlashingInfo.UnbondingTx, + stakingSlashingInfo.StakingTx, + stakingOutIdx, + unbondingPathSpendInfo.GetPkScriptPath(), + covenantSk, + ) + require.NoError(t, err) + covenantUnbondingSig := bbn.NewBIP340SignatureFromBTCSig(unbondingTxCovenantSchnorrSig) + // covenant adaptor sig on unbonding slashing tx + require.NoError(t, err) + covenantSlashingSig, err := unbondingSlashingInfo.SlashingTx.EncSign( + unbondingSlashingInfo.UnbondingTx, + 0, // Only one output in the unbonding transaction + unbondingSlashingPathSpendInfo.GetPkScriptPath(), + covenantSk, + fpEncKey, + ) + require.NoError(t, err) + msgAddCovenantSig := &bstypes.MsgAddCovenantSigs{ + Signer: signerAddr, + Pk: bbn.NewBIP340PubKeyFromBTCPK(covenantSk.PubKey()), + StakingTxHash: stakingMsgTxHash.String(), + SlashingTxSigs: [][]byte{covenantSig.MustMarshal()}, + UnbondingTxSig: covenantUnbondingSig, + SlashingUnbondingTxSigs: [][]byte{covenantSlashingSig.MustMarshal()}, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgAddCovenantSig, nil, nil) + require.NoError(t, err) + t.Logf("submitted covenant signature") +} + +func (tm *TestManager) Undelegate( + t *testing.T, + stakingSlashingInfo *datagen.TestStakingSlashingInfo, + unbondingSlashingInfo *datagen.TestUnbondingSlashingInfo, + delSK *btcec.PrivateKey, + catchUpLightClientFunc func()) (*datagen.TestUnbondingSlashingInfo, *schnorr.Signature) { + signerAddr := tm.BabylonClient.MustGetAddr() + + // TODO: This generates unbonding tx signature, move it to undelegate + unbondingPathSpendInfo, err := stakingSlashingInfo.StakingInfo.UnbondingPathSpendInfo() + require.NoError(t, err) + + // the only input to unbonding tx is the staking tx + stakingOutIdx, err := outIdx(unbondingSlashingInfo.UnbondingTx, unbondingSlashingInfo.UnbondingInfo.UnbondingOutput) + require.NoError(t, err) + unbondingTxSchnorrSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( + unbondingSlashingInfo.UnbondingTx, + stakingSlashingInfo.StakingTx, + stakingOutIdx, + unbondingPathSpendInfo.GetPkScriptPath(), + delSK, + ) + require.NoError(t, err) + + var unbondingTxBuf bytes.Buffer + err = unbondingSlashingInfo.UnbondingTx.Serialize(&unbondingTxBuf) + require.NoError(t, err) + + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + covenantSigs := resp.BtcDelegation.UndelegationResponse.CovenantUnbondingSigList + witness, err := unbondingPathSpendInfo.CreateUnbondingPathWitness( + []*schnorr.Signature{covenantSigs[0].Sig.MustToBTCSig()}, + unbondingTxSchnorrSig, + ) + require.NoError(t, err) + unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness + + // send unbonding tx to Bitcoin node's mempool + unbondingTxHash, err := tm.WalletClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) + require.NoError(t, err) + require.Eventually(t, func() bool { + _, err := tm.WalletClient.GetRawTransaction(unbondingTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + t.Logf("submitted unbonding tx with hash %s", unbondingTxHash.String()) + + // mine a block with this tx, and insert it to Bitcoin + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineBlock(t) + require.Equal(t, 2, len(mBlock.Transactions)) + + catchUpLightClientFunc() + + unbondingTxInfo := getTxInfo(t, mBlock) + msgUndel := &bstypes.MsgBTCUndelegate{ + Signer: signerAddr, + StakingTxHash: stakingSlashingInfo.StakingTx.TxHash().String(), + StakeSpendingTx: unbondingTxBuf.Bytes(), + StakeSpendingTxInclusionProof: &bstypes.InclusionProof{ + Key: unbondingTxInfo.Key, + Proof: unbondingTxInfo.Proof, + }, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgUndel, nil, nil) + require.NoError(t, err) + t.Logf("submitted MsgBTCUndelegate") + + // wait until unbonding tx is on Bitcoin + require.Eventually(t, func() bool { + resp, err := tm.WalletClient.GetRawTransactionVerbose(unbondingTxHash) + if err != nil { + t.Logf("err of GetRawTransactionVerbose: %v", err) + return false + } + return len(resp.BlockHash) > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + return unbondingSlashingInfo, unbondingTxSchnorrSig +} + +func (tm *TestManager) VoteAndEquivocate(t *testing.T, fpSK *btcec.PrivateKey) { + signerAddr := tm.BabylonClient.MustGetAddr() + + // get the finality provider + fpBTCPK := bbn.NewBIP340PubKeyFromBTCPK(fpSK.PubKey()) + fpResp, err := tm.BabylonClient.FinalityProvider(fpBTCPK.MarshalHex()) + require.NoError(t, err) + btcFp := fpResp.FinalityProvider + + _, err = tm.BabylonClient.ActivatedHeight() + require.Error(t, err) + + activatedHeight := uint64(1) + commitStartHeight := activatedHeight + + /* + commit a number of public randomness since activatedHeight + */ + srList, msgCommitPubRandList, err := datagen.GenRandomMsgCommitPubRandList(r, fpSK, activatedHeight, 100) + require.NoError(t, err) + msgCommitPubRandList.Signer = signerAddr + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgCommitPubRandList, nil, nil) + require.NoError(t, err) + t.Logf("committed public randomness") + + tm.mineBlock(t) + + tm.waitForFpPubRandTimestamped(t, fpSK.PubKey()) + + require.Eventually(t, func() bool { + acr, err := tm.BabylonClient.ActivatedHeight() + if err != nil { + return false + } + activatedHeight = acr.Height + return activatedHeight > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + /* + submit finality signature + */ + // get block to vote + blockToVote, err := tm.BabylonClient.GetBlock(int64(activatedHeight)) + require.NoError(t, err) + msgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), blockToVote.Block.AppHash...) + // generate EOTS signature + idx := activatedHeight - commitStartHeight + sig, err := eots.Sign(fpSK, srList.SRList[idx], msgToSign) + require.NoError(t, err) + eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig) + // submit finality signature + msgAddFinalitySig := &ftypes.MsgAddFinalitySig{ + Signer: signerAddr, + FpBtcPk: btcFp.BtcPk, + BlockHeight: activatedHeight, + PubRand: &srList.PRList[idx], + Proof: srList.ProofList[idx].ToProto(), + BlockAppHash: blockToVote.Block.AppHash, + FinalitySig: eotsSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgAddFinalitySig, nil, nil) + require.NoError(t, err) + t.Logf("submitted finality signature") + + /* + equivocate + */ + invalidAppHash := datagen.GenRandomByteArray(r, 32) + invalidMsgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), invalidAppHash...) + invalidSig, err := eots.Sign(fpSK, srList.SRList[idx], invalidMsgToSign) + require.NoError(t, err) + invalidEotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(invalidSig) + invalidMsgAddFinalitySig := &ftypes.MsgAddFinalitySig{ + Signer: signerAddr, + FpBtcPk: btcFp.BtcPk, + BlockHeight: activatedHeight, + PubRand: &srList.PRList[idx], + Proof: srList.ProofList[idx].ToProto(), + BlockAppHash: invalidAppHash, + FinalitySig: invalidEotsSig, + } + _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), invalidMsgAddFinalitySig, nil, nil) + require.NoError(t, err) + t.Logf("submitted equivocating finality signature") +} + +func getTxInfo(t *testing.T, block *wire.MsgBlock) *btcctypes.TransactionInfo { + mHeaderBytes := bbn.NewBTCHeaderBytesFromBlockHeader(&block.Header) + var txBytes [][]byte + for _, tx := range block.Transactions { + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + _ = tx.Serialize(buf) + txBytes = append(txBytes, buf.Bytes()) + } + spvProof, err := btcctypes.SpvProofFromHeaderAndTransactions(&mHeaderBytes, txBytes, 1) + require.NoError(t, err) + return btcctypes.NewTransactionInfoFromSpvProof(spvProof) +} + +// TODO: these functions should be enabled by Babylon +func bbnPksToBtcPks(pks []bbn.BIP340PubKey) ([]*btcec.PublicKey, error) { + btcPks := make([]*btcec.PublicKey, 0, len(pks)) + for _, pk := range pks { + btcPk, err := pk.ToBTCPK() + if err != nil { + return nil, err + } + btcPks = append(btcPks, btcPk) + } + return btcPks, nil +} + +func outIdx(tx *wire.MsgTx, candOut *wire.TxOut) (uint32, error) { + for idx, out := range tx.TxOut { + if bytes.Equal(out.PkScript, candOut.PkScript) && out.Value == candOut.Value { + return uint32(idx), nil + } + } + return 0, fmt.Errorf("couldn't find output") +} + +func (tm *TestManager) waitForFpPubRandTimestamped(t *testing.T, fpPk *btcec.PublicKey) { + var lastCommittedHeight uint64 + var err error + + require.Eventually(t, func() bool { + lastCommittedHeight, err = tm.getLastCommittedHeight(fpPk) + if err != nil { + return false + } + return lastCommittedHeight > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("public randomness is successfully committed, last committed height: %d", lastCommittedHeight) + + // wait until the last registered epoch is finalized + currentEpoch, err := tm.BabylonClient.CurrentEpoch() + require.NoError(t, err) + + tm.finalizeUntilEpoch(t, currentEpoch.CurrentEpoch) + + res, err := tm.BabylonClient.LatestEpochFromStatus(ckpttypes.Finalized) + require.NoError(t, err) + t.Logf("last finalized epoch: %d", res.RawCheckpoint.EpochNum) + + t.Logf("public randomness is successfully timestamped, last finalized epoch: %v", currentEpoch) +} + +// queryLastCommittedPublicRand returns the last public randomness commitments +func (tm *TestManager) queryLastCommittedPublicRand(fpPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { + fpBtcPk := bbn.NewBIP340PubKeyFromBTCPK(fpPk) + + pagination := &sdkquery.PageRequest{ + Limit: count, + Reverse: true, + } + + res, err := tm.BabylonClient.QueryClient.ListPubRandCommit(fpBtcPk.MarshalHex(), pagination) + if err != nil { + return nil, fmt.Errorf("failed to query committed public randomness: %w", err) + } + + return res.PubRandCommitMap, nil +} + +func (tm *TestManager) lastCommittedPublicRandWithRetry(btcPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { + var response map[uint64]*ftypes.PubRandCommitResponse + + if err := retry.Do(func() error { + resp, err := tm.queryLastCommittedPublicRand(btcPk, count) + if err != nil { + return err + } + response = resp + return nil + }, + retry.Attempts(tm.Config.Common.MaxRetryTimes), + retry.Delay(tm.Config.Common.RetrySleepTime), + retry.LastErrorOnly(true)); err != nil { + return nil, err + } + + return response, nil +} + +func (tm *TestManager) getLastCommittedHeight(btcPk *btcec.PublicKey) (uint64, error) { + pubRandCommitMap, err := tm.lastCommittedPublicRandWithRetry(btcPk, 1) + if err != nil { + return 0, err + } + + // no committed randomness yet + if len(pubRandCommitMap) == 0 { + return 0, nil + } + + if len(pubRandCommitMap) > 1 { + return 0, fmt.Errorf("got more than one last committed public randomness") + } + var lastCommittedHeight uint64 + for startHeight, resp := range pubRandCommitMap { + lastCommittedHeight = startHeight + resp.NumPubRand - 1 + } + + return lastCommittedHeight, nil +} + +func (tm *TestManager) finalizeUntilEpoch(t *testing.T, epoch uint64) { + bbnClient := tm.BabylonClient + + // wait until the checkpoint of this epoch is sealed + require.Eventually(t, func() bool { + lastSealedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Sealed) + if err != nil { + return false + } + return epoch <= lastSealedCkpt.RawCheckpoint.EpochNum + }, eventuallyWaitTimeOut, 1*time.Second) + + t.Logf("start finalizing epochs until %d", epoch) + // Random source for the generation of BTC data + r := rand.New(rand.NewSource(time.Now().Unix())) + + // get all checkpoints of these epochs + pagination := &sdkquerytypes.PageRequest{ + Key: ckpttypes.CkptsObjectKey(0), + Limit: epoch, + } + resp, err := bbnClient.RawCheckpoints(pagination) + require.NoError(t, err) + require.Equal(t, int(epoch), len(resp.RawCheckpoints)) + + submitterAddr, err := sdk.AccAddressFromBech32(tm.BabylonClient.MustGetAddr()) + require.NoError(t, err) + + for _, checkpoint := range resp.RawCheckpoints { + currentBtcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() + require.NoError(t, err) + tipHeader, err := bbn.NewBTCHeaderBytesFromHex(currentBtcTipResp.Header.HeaderHex) + require.NoError(t, err) + + rawCheckpoint, err := checkpoint.Ckpt.ToRawCheckpoint() + require.NoError(t, err) + + btcCheckpoint, err := ckpttypes.FromRawCkptToBTCCkpt(rawCheckpoint, submitterAddr) + require.NoError(t, err) + + babylonTagBytes, err := hex.DecodeString("01020304") + require.NoError(t, err) + + p1, p2, err := txformat.EncodeCheckpointData( + babylonTagBytes, + txformat.CurrentVersion, + btcCheckpoint, + ) + require.NoError(t, err) + + tx1 := datagen.CreatOpReturnTransaction(r, p1) + + opReturn1 := datagen.CreateBlockWithTransaction(r, tipHeader.ToBlockHeader(), tx1) + tx2 := datagen.CreatOpReturnTransaction(r, p2) + opReturn2 := datagen.CreateBlockWithTransaction(r, opReturn1.HeaderBytes.ToBlockHeader(), tx2) + + // insert headers and proofs + _, err = tm.insertBtcBlockHeaders([]bbn.BTCHeaderBytes{ + opReturn1.HeaderBytes, + opReturn2.HeaderBytes, + }) + require.NoError(t, err) + + _, err = tm.insertSpvProofs(submitterAddr.String(), []*btcctypes.BTCSpvProof{ + opReturn1.SpvProof, + opReturn2.SpvProof, + }) + require.NoError(t, err) + + // wait until this checkpoint is submitted + require.Eventually(t, func() bool { + ckpt, err := bbnClient.RawCheckpoint(checkpoint.Ckpt.EpochNum) + if err != nil { + return false + } + return ckpt.RawCheckpoint.Status == ckpttypes.Submitted + }, eventuallyWaitTimeOut, eventuallyPollTime) + } + + // insert w BTC headers + tm.insertWBTCHeaders(t, r) + + // wait until the checkpoint of this epoch is finalised + require.Eventually(t, func() bool { + lastFinalizedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Finalized) + if err != nil { + t.Logf("failed to get last finalized epoch: %v", err) + return false + } + return epoch <= lastFinalizedCkpt.RawCheckpoint.EpochNum + }, eventuallyWaitTimeOut, 1*time.Second) + + t.Logf("epoch %d is finalised", epoch) +} + +func (tm *TestManager) insertBtcBlockHeaders(headers []bbn.BTCHeaderBytes) (*provider.RelayerTxResponse, error) { + msg := &btclctypes.MsgInsertHeaders{ + Signer: tm.MustGetBabylonSigner(), + Headers: headers, + } + + res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) + if err != nil { + return nil, err + } + + return res, nil +} + +func (tm *TestManager) insertSpvProofs(submitter string, proofs []*btcctypes.BTCSpvProof) (*provider.RelayerTxResponse, error) { + msg := &btcctypes.MsgInsertBTCSpvProof{ + Submitter: submitter, + Proofs: proofs, + } + + res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) + if err != nil { + return nil, err + } + + return res, nil +} + +func (tm *TestManager) insertWBTCHeaders(t *testing.T, r *rand.Rand) { + ckptParamRes, err := tm.BabylonClient.QueryClient.BTCCheckpointParams() + require.NoError(t, err) + btcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() + require.NoError(t, err) + tipHeader, err := bbn.NewBTCHeaderBytesFromHex(btcTipResp.Header.HeaderHex) + require.NoError(t, err) + kHeaders := datagen.NewBTCHeaderChainFromParentInfo(r, &btclctypes.BTCHeaderInfo{ + Header: &tipHeader, + Hash: tipHeader.Hash(), + Height: btcTipResp.Header.Height, + Work: &btcTipResp.Header.Work, + }, uint32(ckptParamRes.Params.CheckpointFinalizationTimeout)) + _, err = tm.insertBtcBlockHeaders(kHeaders.ChainToBytes()) + require.NoError(t, err) +} diff --git a/internal/types/utxo.go b/internal/types/utxo.go new file mode 100644 index 0000000..75434ef --- /dev/null +++ b/internal/types/utxo.go @@ -0,0 +1,51 @@ +package types + +import ( + "encoding/hex" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" +) + +type UTXO struct { + TxID *chainhash.Hash + Vout uint32 + ScriptPK []byte + Amount btcutil.Amount + Addr btcutil.Address +} + +func NewUTXO(r *btcjson.ListUnspentResult, net *chaincfg.Params) (*UTXO, error) { + prevPKScript, err := hex.DecodeString(r.ScriptPubKey) + if err != nil { + return nil, err + } + txID, err := chainhash.NewHashFromStr(r.TxID) + if err != nil { + return nil, err + } + prevAddr, err := btcutil.DecodeAddress(r.Address, net) + if err != nil { + return nil, err + } + amount, err := btcutil.NewAmount(r.Amount) + if err != nil { + return nil, err + } + + utxo := &UTXO{ + TxID: txID, + Vout: r.Vout, + ScriptPK: prevPKScript, + Amount: amount, + Addr: prevAddr, + } + return utxo, nil +} + +func (u *UTXO) GetOutPoint() *wire.OutPoint { + return wire.NewOutPoint(u.TxID, u.Vout) +} From a13c4691057f902aac75b693fdff98c42e8bd9ca Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 15 Dec 2024 15:57:00 +0530 Subject: [PATCH 11/32] create btcdel/fp --- e2etest/e2e_test.go | 93 ++ e2etest/test_manager.go | 68 +- e2etest/test_manager_btcstaking.go | 29 +- go.mod | 34 +- go.sum | 1767 ++++++++++++++++++++++++++-- internal/config/config.go | 8 + 6 files changed, 1871 insertions(+), 128 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index f7cbe41..301ebee 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -3,16 +3,23 @@ package e2etest import ( "encoding/hex" "encoding/json" + "fmt" "math/rand" + "sync" "testing" "time" bbndatagen "github.com/babylonlabs-io/babylon/testutil/datagen" queuecli "github.com/babylonlabs-io/staking-queue-client/client" "github.com/babylonlabs-io/staking-queue-client/config" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/stretchr/testify/require" ) +var ( + defaultEpochInterval = uint(400) //nolint:unused +) + func TestQueueConsumer(t *testing.T) { // create event consumer queueCfg := config.DefaultQueueConfig() @@ -49,3 +56,89 @@ func TestQueueConsumer(t *testing.T) { require.NoError(t, err) } } + +// TestActivatingDelegation verifies that a delegation created without an inclusion proof will +// eventually become "active". +// Specifically, that stakingEventWatcher will send a MsgAddBTCDelegationInclusionProof to do so. +func TestActivatingDelegation(t *testing.T) { + t.Parallel() + // segwit is activated at height 300. It's necessary for staking/slashing tx + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs, defaultEpochInterval) + defer tm.Stop(t) + // Insert all existing BTC headers to babylon node + tm.CatchUpBTCLightClient(t) + // + //btcNotifier, err := btcclient.NewNodeBackend( + // btcclient.ToBitcoindConfig(tm.Config.BTC), + // &chaincfg.RegressionNetParams, + // &btcclient.EmptyHintCache{}, + //) + //require.NoError(t, err) + // + //err = btcNotifier.Start() + //require.NoError(t, err) + + // commonCfg := config.DefaultCommonConfig() + + // set up a finality provider + _, fpSK := tm.CreateFinalityProvider(t) + // set up a BTC delegation + stakingMsgTx, stakingSlashingInfo, _, _ := tm.CreateBTCDelegationWithoutIncl(t, fpSK) + stakingMsgTxHash := stakingMsgTx.TxHash() + + // send staking tx to Bitcoin node's mempool + _, err := tm.WalletClient.SendRawTransaction(stakingMsgTx, true) + require.NoError(t, err) + + require.Eventually(t, func() bool { + return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&stakingMsgTxHash})) == 1 + }, eventuallyWaitTimeOut, eventuallyPollTime) + + mBlock := tm.mineBlock(t) + require.Equal(t, 2, len(mBlock.Transactions)) + + // wait until staking tx is on Bitcoin + require.Eventually(t, func() bool { + _, err := tm.WalletClient.GetRawTransaction(&stakingMsgTxHash) + return err == nil + }, eventuallyWaitTimeOut, eventuallyPollTime) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + // We want to introduce a latency to make sure that we are not trying to submit inclusion proof while the + // staking tx is not yet K-deep + time.Sleep(10 * time.Second) + // Insert k empty blocks to Bitcoin + btccParamsResp, err := tm.BabylonClient.BTCCheckpointParams() + if err != nil { + fmt.Println("Error fetching BTCCheckpointParams:", err) + return + } + for i := 0; i < int(btccParamsResp.Params.BtcConfirmationDepth); i++ { + tm.mineBlock(t) + } + tm.CatchUpBTCLightClient(t) + }() + + wg.Wait() + + // // make sure we didn't submit any "invalid" incl proof + // require.Eventually(t, func() bool { + // return promtestutil.ToFloat64(stakingTrackerMetrics.FailedReportedActivateDelegations) == 0 + // }, eventuallyWaitTimeOut, eventuallyPollTime) + + // created delegation lacks inclusion proof, once created it will be in + // pending status, once convenant signatures are added it will be in verified status, + // and once the stakingEventWatcher submits MsgAddBTCDelegationInclusionProof it will + // be in active status + require.Eventually(t, func() bool { + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + require.NoError(t, err) + + return resp.BtcDelegation.Active + }, eventuallyWaitTimeOut, eventuallyPollTime) +} diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index a8c953b..d90b114 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/hex" + "encoding/json" "fmt" "os" "path/filepath" @@ -13,8 +14,10 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/e2etest/container" "github.com/babylonlabs-io/babylon-staking-indexer/internal/clients/btcclient" "github.com/babylonlabs-io/babylon-staking-indexer/internal/config" + _ "github.com/babylonlabs-io/babylon/app/params" bbnclient "github.com/babylonlabs-io/babylon/client/client" bbncfg "github.com/babylonlabs-io/babylon/client/config" + _ "github.com/babylonlabs-io/babylon/types" bbn "github.com/babylonlabs-io/babylon/types" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" "github.com/btcsuite/btcd/btcec/v2" @@ -59,9 +62,9 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u bitcoind := btcHandler.Start(t) passphrase := "pass" _ = btcHandler.CreateWallet("default", passphrase) - resp := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) - minerAddressDecoded, err := btcutil.DecodeAddress(resp.Address, regtestParams) - require.NoError(t, err) + // resp := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) + // minerAddressDecoded, err := btcutil.DecodeAddress(resp.Address, regtestParams) + // require.NoError(t, err) cfg := DefaultStakingIndexerConfig() @@ -71,9 +74,16 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u require.NoError(t, err) rpcclient, err := rpcclient.New(connCfg, nil) require.NoError(t, err) - err = rpcclient.WalletPassphrase(passphrase, 200) + err = rpcclient.WalletPassphrase(passphrase, 800) + require.NoError(t, err) + // walletPrivKey, err := rpcclient.DumpPrivKey(minerAddressDecoded) + // require.NoError(t, err) + + walletPrivKey, err := importPrivateKey(btcHandler) require.NoError(t, err) - walletPrivKey, err := rpcclient.DumpPrivKey(minerAddressDecoded) + blocksResponse := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) + + minerAddressDecoded, err := btcutil.DecodeAddress(blocksResponse.Address, regtestParams) require.NoError(t, err) var buff bytes.Buffer @@ -108,9 +118,11 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u // wait until Babylon is ready require.Eventually(t, func() bool { - _, err := babylonClient.CurrentEpoch() - require.NoError(t, err) - //log.Infof("Babylon is ready: %v", resp) + resp, err := babylonClient.CurrentEpoch() + if err != nil { + return false + } + fmt.Println(resp) return true }, eventuallyWaitTimeOut, eventuallyPollTime) @@ -125,7 +137,7 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u BitcoindHandler: btcHandler, BTCClient: btcClient, Config: cfg, - WalletPrivKey: walletPrivKey.PrivKey, + WalletPrivKey: walletPrivKey, manager: manager, } } @@ -222,6 +234,11 @@ func (tm *TestManager) CatchUpBTCLightClient(t *testing.T) { headers = append(headers, header) } + // Or with JSON formatting + configJSON, err := json.MarshalIndent(tm.Config, "", " ") + require.NoError(t, err) + t.Logf("Full Config JSON:\n%s", string(configJSON)) + _, err = tm.InsertBTCHeadersToBabylon(headers) require.NoError(t, err) } @@ -240,3 +257,36 @@ func (tm *TestManager) InsertBTCHeadersToBabylon(headers []*wire.BlockHeader) (* return tm.BabylonClient.InsertHeaders(context.Background(), &msg) } + +func importPrivateKey(btcHandler *BitcoindTestHandler) (*btcec.PrivateKey, error) { + privKey, err := btcec.NewPrivateKey() + if err != nil { + return nil, err + } + + wif, err := btcutil.NewWIF(privKey, regtestParams, true) + if err != nil { + return nil, err + } + + // "combo" allows us to import a key and handle multiple types of btc scripts with a single descriptor command. + descriptor := fmt.Sprintf("combo(%s)", wif.String()) + + // Create the JSON descriptor object. + descJSON, err := json.Marshal([]map[string]interface{}{ + { + "desc": descriptor, + "active": true, + "timestamp": "now", // tells Bitcoind to start scanning from the current blockchain height + "label": "test key", + }, + }) + + if err != nil { + return nil, err + } + + btcHandler.ImportDescriptors(string(descJSON)) + + return privKey, nil +} diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index ee0d276..f4c9453 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -25,6 +25,7 @@ import ( ftypes "github.com/babylonlabs-io/babylon/x/finality/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" @@ -97,7 +98,7 @@ func (tm *TestManager) CreateBTCDelegation( require.NoError(t, err) stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks // get top UTXO - topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() + topUnspentResult, _, err := tm.getHighUTXOAndSum() require.NoError(t, err) topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) require.NoError(t, err) @@ -229,7 +230,7 @@ func (tm *TestManager) CreateBTCDelegationWithoutIncl( require.NoError(t, err) stakingTimeBlocks := bsParams.Params.MaxStakingTimeBlocks // get top UTXO - topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() + topUnspentResult, _, err := tm.getHighUTXOAndSum() require.NoError(t, err) topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) require.NoError(t, err) @@ -726,8 +727,8 @@ func (tm *TestManager) lastCommittedPublicRandWithRetry(btcPk *btcec.PublicKey, response = resp return nil }, - retry.Attempts(tm.Config.Common.MaxRetryTimes), - retry.Delay(tm.Config.Common.RetrySleepTime), + retry.Attempts(5), + retry.Delay(1*time.Second), retry.LastErrorOnly(true)); err != nil { return nil, err } @@ -896,3 +897,23 @@ func (tm *TestManager) insertWBTCHeaders(t *testing.T, r *rand.Rand) { _, err = tm.insertBtcBlockHeaders(kHeaders.ChainToBytes()) require.NoError(t, err) } + +func (tm *TestManager) getHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, error) { + utxos, err := tm.WalletClient.ListUnspent() + if err != nil { + return nil, 0, fmt.Errorf("failed to list unspent UTXOs: %w", err) + } + if len(utxos) == 0 { + return nil, 0, fmt.Errorf("lack of spendable transactions in the wallet") + } + + highUTXO := utxos[0] // freshest UTXO + sum := float64(0) + for _, utxo := range utxos { + if highUTXO.Amount < utxo.Amount { + highUTXO = utxo + } + sum += utxo.Amount + } + return &highUTXO, sum, nil +} diff --git a/go.mod b/go.mod index 0165b3f..67f1da6 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/babylonlabs-io/babylon-staking-indexer go 1.23.1 require ( + cosmossdk.io/math v1.4.0 github.com/avast/retry-go/v4 v4.5.1 github.com/babylonlabs-io/babylon v0.18.0 github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20241212112557-9ac7de686075 @@ -14,9 +15,11 @@ require ( github.com/cometbft/cometbft v0.38.15 github.com/cosmos/cosmos-sdk v0.50.9 github.com/cosmos/gogoproto v1.7.0 + github.com/cosmos/relayer/v2 v2.5.2 github.com/go-chi/chi/v5 v5.1.0 github.com/lightningnetwork/lnd v0.17.0-beta github.com/ory/dockertest/v3 v3.10.0 + github.com/rabbitmq/amqp091-go v1.9.0 github.com/spf13/viper v1.19.0 go.uber.org/zap v1.27.0 golang.org/x/mod v0.17.0 @@ -34,7 +37,6 @@ require ( cosmossdk.io/depinject v1.0.0 // indirect cosmossdk.io/errors v1.0.1 // indirect cosmossdk.io/log v1.4.1 // indirect - cosmossdk.io/math v1.4.0 // indirect cosmossdk.io/store v1.1.0 // indirect cosmossdk.io/x/circuit v0.1.1 // indirect cosmossdk.io/x/evidence v0.1.1 // indirect @@ -44,7 +46,6 @@ require ( cosmossdk.io/x/upgrade v0.1.4 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect - github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/CosmWasm/wasmd v0.53.0 // indirect @@ -95,7 +96,6 @@ require ( github.com/cosmos/ibc-go/v8 v8.4.0 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect - github.com/cosmos/relayer/v2 v2.5.2 // indirect github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -134,7 +134,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/flatbuffers v1.12.1 // indirect + github.com/google/flatbuffers v23.5.26+incompatible // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/orderedcode v0.0.1 // indirect @@ -149,6 +149,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.5 // indirect @@ -186,6 +187,7 @@ require ( github.com/jsternberg/zap-logfmt v1.3.0 // indirect github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect github.com/kkdai/bstream v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/pgzip v1.2.5 // indirect @@ -223,15 +225,14 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/opencontainers/runc v1.1.12 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect - github.com/pierrec/lz4/v4 v4.1.8 // indirect + github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rabbitmq/amqp091-go v1.9.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/cors v1.11.1 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect @@ -275,7 +276,7 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/sdk v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect - go.opentelemetry.io/proto/otlp v0.9.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect golang.org/x/crypto v0.28.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect @@ -292,16 +293,16 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools/v3 v3.5.1 // indirect - lukechampine.com/uint128 v1.2.0 // indirect + lukechampine.com/uint128 v1.3.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.22.2 // indirect + modernc.org/libc v1.22.4 // indirect modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.4.0 // indirect + modernc.org/memory v1.5.0 // indirect modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.20.3 // indirect + modernc.org/sqlite v1.21.2 // indirect modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.1 // indirect + modernc.org/token v1.1.0 // indirect nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect @@ -337,4 +338,9 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/btcsuite/btcd/btcec/v2 => github.com/btcsuite/btcd/btcec/v2 v2.3.2 +replace ( + github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 + github.com/btcsuite/btcd/btcec/v2 => github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + google.golang.org/grpc => google.golang.org/grpc v1.63.2 +) diff --git a/go.sum b/go.sum index 971e58d..03e65ed 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -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.44.3/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= @@ -15,6 +15,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY 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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 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= @@ -26,28 +27,201 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= +cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= +cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= +cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= +cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= +cloud.google.com/go/accessapproval v1.7.3/go.mod h1:4l8+pwIxGTNqSf4T3ds8nLO94NQf0W/KnMNuQ9PbnP8= +cloud.google.com/go/accessapproval v1.7.4/go.mod h1:/aTEh45LzplQgFYdQdwPMR9YdX0UlhBmvB84uAmQKUc= +cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= +cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= +cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= +cloud.google.com/go/accesscontextmanager v1.8.3/go.mod h1:4i/JkF2JiFbhLnnpnfoTX5vRXfhf9ukhU1ANOTALTOQ= +cloud.google.com/go/accesscontextmanager v1.8.4/go.mod h1:ParU+WbMpD34s5JFEnGAnPBYAgUHozaTmDJU7aCU9+M= +cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= +cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= +cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= +cloud.google.com/go/aiplatform v1.51.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= +cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= +cloud.google.com/go/aiplatform v1.51.2/go.mod h1:hCqVYB3mY45w99TmetEoe8eCQEwZEp9WHxeZdcv9phw= +cloud.google.com/go/aiplatform v1.52.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= +cloud.google.com/go/aiplatform v1.54.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= +cloud.google.com/go/aiplatform v1.57.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= +cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= +cloud.google.com/go/aiplatform v1.58.2/go.mod h1:c3kCiVmb6UC1dHAjZjcpDj6ZS0bHQ2slL88ZjC2LtlA= +cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= +cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= +cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= +cloud.google.com/go/analytics v0.21.5/go.mod h1:BQtOBHWTlJ96axpPPnw5CvGJ6i3Ve/qX2fTxR8qWyr8= +cloud.google.com/go/analytics v0.21.6/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= +cloud.google.com/go/analytics v0.22.0/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= +cloud.google.com/go/analytics v0.23.0/go.mod h1:YPd7Bvik3WS95KBok2gPXDqQPHy08TsCQG6CdUCb+u0= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= +cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= +cloud.google.com/go/apigateway v1.6.3/go.mod h1:k68PXWpEs6BVDTtnLQAyG606Q3mz8pshItwPXjgv44Y= +cloud.google.com/go/apigateway v1.6.4/go.mod h1:0EpJlVGH5HwAN4VF4Iec8TAzGN1aQgbxAWGJsnPCGGY= +cloud.google.com/go/apigateway v1.6.5/go.mod h1:6wCwvYRckRQogyDDltpANi3zsCDl6kWi0b4Je+w2UiI= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= +cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= +cloud.google.com/go/apigeeconnect v1.6.3/go.mod h1:peG0HFQ0si2bN15M6QSjEW/W7Gy3NYkWGz7pFz13cbo= +cloud.google.com/go/apigeeconnect v1.6.4/go.mod h1:CapQCWZ8TCjnU0d7PobxhpOdVz/OVJ2Hr/Zcuu1xFx0= +cloud.google.com/go/apigeeconnect v1.6.5/go.mod h1:MEKm3AiT7s11PqTfKE3KZluZA9O91FNysvd3E6SJ6Ow= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= +cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= +cloud.google.com/go/apigeeregistry v0.8.1/go.mod h1:MW4ig1N4JZQsXmBSwH4rwpgDonocz7FPBSw6XPGHmYw= +cloud.google.com/go/apigeeregistry v0.8.2/go.mod h1:h4v11TDGdeXJDJvImtgK2AFVvMIgGWjSb0HRnBSjcX8= +cloud.google.com/go/apigeeregistry v0.8.3/go.mod h1:aInOWnqF4yMQx8kTjDqHNXjZGh/mxeNlAf52YqtASUs= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= +cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= +cloud.google.com/go/appengine v1.8.3/go.mod h1:2oUPZ1LVZ5EXi+AF1ihNAF+S8JrzQ3till5m9VQkrsk= +cloud.google.com/go/appengine v1.8.4/go.mod h1:TZ24v+wXBujtkK77CXCpjZbnuTvsFNT41MUaZ28D6vg= +cloud.google.com/go/appengine v1.8.5/go.mod h1:uHBgNoGLTS5di7BvU25NFDuKa82v0qQLjyMJLuPQrVo= cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= +cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= +cloud.google.com/go/area120 v0.8.3/go.mod h1:5zj6pMzVTH+SVHljdSKC35sriR/CVvQZzG/Icdyriw0= +cloud.google.com/go/area120 v0.8.4/go.mod h1:jfawXjxf29wyBXr48+W+GyX/f8fflxp642D/bb9v68M= +cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5UFu6GMyA0= cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= +cloud.google.com/go/artifactregistry v1.14.2/go.mod h1:Xk+QbsKEb0ElmyeMfdHAey41B+qBq3q5R5f5xD4XT3U= +cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= +cloud.google.com/go/artifactregistry v1.14.4/go.mod h1:SJJcZTMv6ce0LDMUnihCN7WSrI+kBSFV0KIKo8S8aYU= +cloud.google.com/go/artifactregistry v1.14.6/go.mod h1:np9LSFotNWHcjnOgh8UVK0RFPCTUGbO0ve3384xyHfE= +cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= +cloud.google.com/go/asset v1.15.0/go.mod h1:tpKafV6mEut3+vN9ScGvCHXHj7FALFVta+okxFECHcg= +cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= +cloud.google.com/go/asset v1.15.2/go.mod h1:B6H5tclkXvXz7PD22qCA2TDxSVQfasa3iDlM89O2NXs= +cloud.google.com/go/asset v1.15.3/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= +cloud.google.com/go/asset v1.16.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= +cloud.google.com/go/asset v1.17.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= +cloud.google.com/go/asset v1.17.1/go.mod h1:byvDw36UME5AzGNK7o4JnOnINkwOZ1yRrGrKIahHrng= +cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= +cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= +cloud.google.com/go/assuredworkloads v1.11.3/go.mod h1:vEjfTKYyRUaIeA0bsGJceFV2JKpVRgyG2op3jfa59Zs= +cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= +cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= +cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= +cloud.google.com/go/automl v1.13.3/go.mod h1:Y8KwvyAZFOsMAPqUCfNu1AyclbC6ivCUF/MTwORymyY= +cloud.google.com/go/automl v1.13.4/go.mod h1:ULqwX/OLZ4hBVfKQaMtxMSTlPx0GqGbWN8uA/1EqCP8= +cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= +cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= +cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= +cloud.google.com/go/baremetalsolution v1.2.2/go.mod h1:O5V6Uu1vzVelYahKfwEWRMaS3AbCkeYHy3145s1FkhM= +cloud.google.com/go/baremetalsolution v1.2.3/go.mod h1:/UAQ5xG3faDdy180rCUv47e0jvpp3BFxT+Cl0PFjw5g= +cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= +cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= +cloud.google.com/go/batch v1.5.0/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= +cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= +cloud.google.com/go/batch v1.6.1/go.mod h1:urdpD13zPe6YOK+6iZs/8/x2VBRofvblLpx0t57vM98= +cloud.google.com/go/batch v1.6.3/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= +cloud.google.com/go/batch v1.7.0/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= +cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= +cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= +cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= +cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= +cloud.google.com/go/beyondcorp v1.0.2/go.mod h1:m8cpG7caD+5su+1eZr+TSvF6r21NdLJk4f9u4SP2Ntc= +cloud.google.com/go/beyondcorp v1.0.3/go.mod h1:HcBvnEd7eYr+HGDd5ZbuVmBYX019C6CEXBonXbCVwJo= +cloud.google.com/go/beyondcorp v1.0.4/go.mod h1:Gx8/Rk2MxrvWfn4WIhHIG1NV7IBfg14pTKv1+EArVcc= 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= @@ -55,12 +229,98 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM 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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= +cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= +cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= +cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= +cloud.google.com/go/bigquery v1.57.1/go.mod h1:iYzC0tGVWt1jqSzBHqCr3lrRn0u13E8e+AqowBsDgug= +cloud.google.com/go/bigquery v1.58.0/go.mod h1:0eh4mWNY0KrBTjUzLjoYImapGORq9gEPT7MWjCy9lik= +cloud.google.com/go/bigquery v1.59.1/go.mod h1:VP1UJYgevyTwsV7desjzNzDND5p6hZB+Z8gZJN1GQUc= cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= +cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= +cloud.google.com/go/billing v1.17.1/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= +cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= +cloud.google.com/go/billing v1.17.3/go.mod h1:z83AkoZ7mZwBGT3yTnt6rSGI1OOsHSIi6a5M3mJ8NaU= +cloud.google.com/go/billing v1.17.4/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= +cloud.google.com/go/billing v1.18.0/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= +cloud.google.com/go/billing v1.18.2/go.mod h1:PPIwVsOOQ7xzbADCwNe8nvK776QpfrOAUkvKjCUcpSE= cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= +cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= +cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= +cloud.google.com/go/binaryauthorization v1.7.2/go.mod h1:kFK5fQtxEp97m92ziy+hbu+uKocka1qRRL8MVJIgjv0= +cloud.google.com/go/binaryauthorization v1.7.3/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= +cloud.google.com/go/binaryauthorization v1.8.0/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= +cloud.google.com/go/binaryauthorization v1.8.1/go.mod h1:1HVRyBerREA/nhI7yLang4Zn7vfNVA3okoAR9qYQJAQ= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= +cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= +cloud.google.com/go/certificatemanager v1.7.3/go.mod h1:T/sZYuC30PTag0TLo28VedIRIj1KPGcOQzjWAptHa00= +cloud.google.com/go/certificatemanager v1.7.4/go.mod h1:FHAylPe/6IIKuaRmHbjbdLhGhVQ+CWHSD5Jq0k4+cCE= +cloud.google.com/go/certificatemanager v1.7.5/go.mod h1:uX+v7kWqy0Y3NG/ZhNvffh0kuqkKZIXdvlZRO7z0VtM= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= +cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= +cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= +cloud.google.com/go/channel v1.17.2/go.mod h1:aT2LhnftnyfQceFql5I/mP8mIbiiJS4lWqgXA815zMk= +cloud.google.com/go/channel v1.17.3/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= +cloud.google.com/go/channel v1.17.4/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= +cloud.google.com/go/channel v1.17.5/go.mod h1:FlpaOSINDAXgEext0KMaBq/vwpLMkkPAw9b2mApQeHc= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= +cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= +cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= +cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= +cloud.google.com/go/cloudbuild v1.14.2/go.mod h1:Bn6RO0mBYk8Vlrt+8NLrru7WXlQ9/RDWz2uo5KG1/sg= +cloud.google.com/go/cloudbuild v1.14.3/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= +cloud.google.com/go/cloudbuild v1.15.0/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= +cloud.google.com/go/cloudbuild v1.15.1/go.mod h1:gIofXZSu+XD2Uy+qkOrGKEx45zd7s28u/k8f99qKals= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= +cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= +cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= +cloud.google.com/go/clouddms v1.7.2/go.mod h1:Rk32TmWmHo64XqDvW7jgkFQet1tUKNVzs7oajtJT3jU= +cloud.google.com/go/clouddms v1.7.3/go.mod h1:fkN2HQQNUYInAU3NQ3vRLkV2iWs8lIdmBKOx4nrL6Hc= +cloud.google.com/go/clouddms v1.7.4/go.mod h1:RdrVqoFG9RWI5AvZ81SxJ/xvxPdtcRhFotwdE79DieY= cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= +cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= +cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= +cloud.google.com/go/cloudtasks v1.12.3/go.mod h1:GPVXhIOSGEaR+3xT4Fp72ScI+HjHffSS4B8+BaBB5Ys= +cloud.google.com/go/cloudtasks v1.12.4/go.mod h1:BEPu0Gtt2dU6FxZHNqqNdGqIG86qyWKBPGnsb7udGY0= +cloud.google.com/go/cloudtasks v1.12.6/go.mod h1:b7c7fe4+TJsFZfDyzO51F7cjq7HLUlRi/KZQLQjDsaY= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= @@ -68,120 +328,990 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= +cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= +cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= +cloud.google.com/go/contactcenterinsights v1.11.0/go.mod h1:hutBdImE4XNZ1NV4vbPJKSFOnQruhC5Lj9bZqWMTKiU= +cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= +cloud.google.com/go/contactcenterinsights v1.11.2/go.mod h1:A9PIR5ov5cRcd28KlDbmmXE8Aay+Gccer2h4wzkYFso= +cloud.google.com/go/contactcenterinsights v1.11.3/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= +cloud.google.com/go/contactcenterinsights v1.12.0/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= +cloud.google.com/go/contactcenterinsights v1.12.1/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= +cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= +cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= +cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= +cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= +cloud.google.com/go/container v1.26.2/go.mod h1:YlO84xCt5xupVbLaMY4s3XNE79MUJ+49VmkInr6HvF4= +cloud.google.com/go/container v1.27.1/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= +cloud.google.com/go/container v1.28.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= +cloud.google.com/go/container v1.29.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= +cloud.google.com/go/container v1.30.1/go.mod h1:vkbfX0EnAKL/vgVECs5BZn24e1cJROzgszJirRKQ4Bg= +cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= +cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= +cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= +cloud.google.com/go/containeranalysis v0.11.2/go.mod h1:xibioGBC1MD2j4reTyV1xY1/MvKaz+fyM9ENWhmIeP8= +cloud.google.com/go/containeranalysis v0.11.3/go.mod h1:kMeST7yWFQMGjiG9K7Eov+fPNQcGhb8mXj/UcTiWw9U= +cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= +cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= +cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= +cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= +cloud.google.com/go/datacatalog v1.18.0/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= +cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= +cloud.google.com/go/datacatalog v1.18.2/go.mod h1:SPVgWW2WEMuWHA+fHodYjmxPiMqcOiWfhc9OD5msigk= +cloud.google.com/go/datacatalog v1.18.3/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= +cloud.google.com/go/datacatalog v1.19.0/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= +cloud.google.com/go/datacatalog v1.19.2/go.mod h1:2YbODwmhpLM4lOFe3PuEhHK9EyTzQJ5AXgIy7EDKTEE= +cloud.google.com/go/datacatalog v1.19.3/go.mod h1:ra8V3UAsciBpJKQ+z9Whkxzxv7jmQg1hfODr3N3YPJ4= cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= +cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= +cloud.google.com/go/dataflow v0.9.3/go.mod h1:HI4kMVjcHGTs3jTHW/kv3501YW+eloiJSLxkJa/vqFE= +cloud.google.com/go/dataflow v0.9.4/go.mod h1:4G8vAkHYCSzU8b/kmsoR2lWyHJD85oMJPHMtan40K8w= +cloud.google.com/go/dataflow v0.9.5/go.mod h1:udl6oi8pfUHnL0z6UN9Lf9chGqzDMVqcYTcZ1aPnCZQ= cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= +cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= +cloud.google.com/go/dataform v0.8.3/go.mod h1:8nI/tvv5Fso0drO3pEjtowz58lodx8MVkdV2q0aPlqg= +cloud.google.com/go/dataform v0.9.1/go.mod h1:pWTg+zGQ7i16pyn0bS1ruqIE91SdL2FDMvEYu/8oQxs= +cloud.google.com/go/dataform v0.9.2/go.mod h1:S8cQUwPNWXo7m/g3DhWHsLBoufRNn9EgFrMgne2j7cI= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= +cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= +cloud.google.com/go/datafusion v1.7.3/go.mod h1:eoLt1uFXKGBq48jy9LZ+Is8EAVLnmn50lNncLzwYokE= +cloud.google.com/go/datafusion v1.7.4/go.mod h1:BBs78WTOLYkT4GVZIXQCZT3GFpkpDN4aBY4NDX/jVlM= +cloud.google.com/go/datafusion v1.7.5/go.mod h1:bYH53Oa5UiqahfbNK9YuYKteeD4RbQSNMx7JF7peGHc= cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= +cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= +cloud.google.com/go/datalabeling v0.8.3/go.mod h1:tvPhpGyS/V7lqjmb3V0TaDdGvhzgR1JoW7G2bpi2UTI= +cloud.google.com/go/datalabeling v0.8.4/go.mod h1:Z1z3E6LHtffBGrNUkKwbwbDxTiXEApLzIgmymj8A3S8= +cloud.google.com/go/datalabeling v0.8.5/go.mod h1:IABB2lxQnkdUbMnQaOl2prCOfms20mcPxDBm36lps+s= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= +cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= +cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= +cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= +cloud.google.com/go/dataplex v1.10.2/go.mod h1:xdC8URdTrCrZMW6keY779ZT1cTOfV8KEPNsw+LTRT1Y= +cloud.google.com/go/dataplex v1.11.1/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= +cloud.google.com/go/dataplex v1.11.2/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= +cloud.google.com/go/dataplex v1.13.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= +cloud.google.com/go/dataplex v1.14.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= +cloud.google.com/go/dataplex v1.14.1/go.mod h1:bWxQAbg6Smg+sca2+Ex7s8D9a5qU6xfXtwmq4BVReps= +cloud.google.com/go/dataplex v1.14.2/go.mod h1:0oGOSFlEKef1cQeAHXy4GZPB/Ife0fz/PxBf+ZymA2U= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= +cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= +cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= +cloud.google.com/go/dataproc/v2 v2.2.2/go.mod h1:aocQywVmQVF4i8CL740rNI/ZRpsaaC1Wh2++BJ7HEJ4= +cloud.google.com/go/dataproc/v2 v2.2.3/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= +cloud.google.com/go/dataproc/v2 v2.3.0/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= +cloud.google.com/go/dataproc/v2 v2.4.0/go.mod h1:3B1Ht2aRB8VZIteGxQS/iNSJGzt9+CA0WGnDVMEm7Z4= cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= +cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= +cloud.google.com/go/dataqna v0.8.3/go.mod h1:wXNBW2uvc9e7Gl5k8adyAMnLush1KVV6lZUhB+rqNu4= +cloud.google.com/go/dataqna v0.8.4/go.mod h1:mySRKjKg5Lz784P6sCov3p1QD+RZQONRMRjzGNcFd0c= +cloud.google.com/go/dataqna v0.8.5/go.mod h1:vgihg1mz6n7pb5q2YJF7KlXve6tCglInd6XO0JGOlWM= 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/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= +cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= +cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= +cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= +cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= +cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= +cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= +cloud.google.com/go/datastream v1.10.2/go.mod h1:W42TFgKAs/om6x/CdXX5E4oiAsKlH+e8MTGy81zdYt0= +cloud.google.com/go/datastream v1.10.3/go.mod h1:YR0USzgjhqA/Id0Ycu1VvZe8hEWwrkjuXrGbzeDOSEA= +cloud.google.com/go/datastream v1.10.4/go.mod h1:7kRxPdxZxhPg3MFeCSulmAJnil8NJGGvSNdn4p1sRZo= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= +cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= +cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= +cloud.google.com/go/deploy v1.14.1/go.mod h1:N8S0b+aIHSEeSr5ORVoC0+/mOPUysVt8ae4QkZYolAw= +cloud.google.com/go/deploy v1.14.2/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= +cloud.google.com/go/deploy v1.15.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= +cloud.google.com/go/deploy v1.16.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= +cloud.google.com/go/deploy v1.17.0/go.mod h1:XBr42U5jIr64t92gcpOXxNrqL2PStQCXHuKK5GRUuYo= +cloud.google.com/go/deploy v1.17.1/go.mod h1:SXQyfsXrk0fBmgBHRzBjQbZhMfKZ3hMQBw5ym7MN/50= cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= +cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= +cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= +cloud.google.com/go/dialogflow v1.44.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= +cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= +cloud.google.com/go/dialogflow v1.44.2/go.mod h1:QzFYndeJhpVPElnFkUXxdlptx0wPnBWLCBT9BvtC3/c= +cloud.google.com/go/dialogflow v1.44.3/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= +cloud.google.com/go/dialogflow v1.47.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= +cloud.google.com/go/dialogflow v1.48.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= +cloud.google.com/go/dialogflow v1.48.1/go.mod h1:C1sjs2/g9cEwjCltkKeYp3FFpz8BOzNondEaAlCpt+A= +cloud.google.com/go/dialogflow v1.48.2/go.mod h1:7A2oDf6JJ1/+hdpnFRfb/RjJUOh2X3rhIa5P8wQSEX4= +cloud.google.com/go/dialogflow v1.49.0/go.mod h1:dhVrXKETtdPlpPhE7+2/k4Z8FRNUp6kMV3EW3oz/fe0= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= +cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= +cloud.google.com/go/dlp v1.10.3/go.mod h1:iUaTc/ln8I+QT6Ai5vmuwfw8fqTk2kaz0FvCwhLCom0= +cloud.google.com/go/dlp v1.11.1/go.mod h1:/PA2EnioBeXTL/0hInwgj0rfsQb3lpE3R8XUJxqUNKI= +cloud.google.com/go/dlp v1.11.2/go.mod h1:9Czi+8Y/FegpWzgSfkRlyz+jwW6Te9Rv26P3UfU/h/w= cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= +cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= +cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= +cloud.google.com/go/documentai v1.23.0/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= +cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= +cloud.google.com/go/documentai v1.23.4/go.mod h1:4MYAaEMnADPN1LPN5xboDR5QVB6AgsaxgFdJhitlE2Y= +cloud.google.com/go/documentai v1.23.5/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= +cloud.google.com/go/documentai v1.23.6/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= +cloud.google.com/go/documentai v1.23.7/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= +cloud.google.com/go/documentai v1.23.8/go.mod h1:Vd/y5PosxCpUHmwC+v9arZyeMfTqBR9VIwOwIqQYYfA= +cloud.google.com/go/documentai v1.25.0/go.mod h1:ftLnzw5VcXkLItp6pw1mFic91tMRyfv6hHEY5br4KzY= cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= +cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= +cloud.google.com/go/domains v0.9.3/go.mod h1:29k66YNDLDY9LCFKpGFeh6Nj9r62ZKm5EsUJxAl84KU= +cloud.google.com/go/domains v0.9.4/go.mod h1:27jmJGShuXYdUNjyDG0SodTfT5RwLi7xmH334Gvi3fY= +cloud.google.com/go/domains v0.9.5/go.mod h1:dBzlxgepazdFhvG7u23XMhmMKBjrkoUNaw0A8AQB55Y= cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= +cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= +cloud.google.com/go/edgecontainer v1.1.3/go.mod h1:Ll2DtIABzEfaxaVSbwj3QHFaOOovlDFiWVDu349jSsA= +cloud.google.com/go/edgecontainer v1.1.4/go.mod h1:AvFdVuZuVGdgaE5YvlL1faAoa1ndRR/5XhXZvPBHbsE= +cloud.google.com/go/edgecontainer v1.1.5/go.mod h1:rgcjrba3DEDEQAidT4yuzaKWTbkTI5zAMu3yy6ZWS0M= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= +cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= +cloud.google.com/go/essentialcontacts v1.6.4/go.mod h1:iju5Vy3d9tJUg0PYMd1nHhjV7xoCXaOAVabrwLaPBEM= +cloud.google.com/go/essentialcontacts v1.6.5/go.mod h1:jjYbPzw0x+yglXC890l6ECJWdYeZ5dlYACTFL0U/VuM= +cloud.google.com/go/essentialcontacts v1.6.6/go.mod h1:XbqHJGaiH0v2UvtuucfOzFXN+rpL/aU5BCZLn4DYl1Q= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= +cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= +cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= +cloud.google.com/go/eventarc v1.13.2/go.mod h1:X9A80ShVu19fb4e5sc/OLV7mpFUKZMwfJFeeWhcIObM= +cloud.google.com/go/eventarc v1.13.3/go.mod h1:RWH10IAZIRcj1s/vClXkBgMHwh59ts7hSWcqD3kaclg= +cloud.google.com/go/eventarc v1.13.4/go.mod h1:zV5sFVoAa9orc/52Q+OuYUG9xL2IIZTbbuTHC6JSY8s= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= +cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= +cloud.google.com/go/filestore v1.7.3/go.mod h1:Qp8WaEERR3cSkxToxFPHh/b8AACkSut+4qlCjAmKTV0= +cloud.google.com/go/filestore v1.7.4/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= +cloud.google.com/go/filestore v1.8.0/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= +cloud.google.com/go/filestore v1.8.1/go.mod h1:MbN9KcaM47DRTIuLfQhJEsjaocVebNtNQhSLhKCF5GM= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= +cloud.google.com/go/firestore v1.12.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= +cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= +cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= +cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= +cloud.google.com/go/functions v1.15.3/go.mod h1:r/AMHwBheapkkySEhiZYLDBwVJCdlRwsm4ieJu35/Ug= +cloud.google.com/go/functions v1.15.4/go.mod h1:CAsTc3VlRMVvx+XqXxKqVevguqJpnVip4DdonFsX28I= +cloud.google.com/go/functions v1.16.0/go.mod h1:nbNpfAG7SG7Duw/o1iZ6ohvL7mc6MapWQVpqtM29n8k= cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= +cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= +cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= +cloud.google.com/go/gkebackup v1.3.3/go.mod h1:eMk7/wVV5P22KBakhQnJxWSVftL1p4VBFLpv0kIft7I= +cloud.google.com/go/gkebackup v1.3.4/go.mod h1:gLVlbM8h/nHIs09ns1qx3q3eaXcGSELgNu1DWXYz1HI= +cloud.google.com/go/gkebackup v1.3.5/go.mod h1:KJ77KkNN7Wm1LdMopOelV6OodM01pMuK2/5Zt1t4Tvc= cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= +cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= +cloud.google.com/go/gkeconnect v0.8.3/go.mod h1:i9GDTrfzBSUZGCe98qSu1B8YB8qfapT57PenIb820Jo= +cloud.google.com/go/gkeconnect v0.8.4/go.mod h1:84hZz4UMlDCKl8ifVW8layK4WHlMAFeq8vbzjU0yJkw= +cloud.google.com/go/gkeconnect v0.8.5/go.mod h1:LC/rS7+CuJ5fgIbXv8tCD/mdfnlAadTaUufgOkmijuk= cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= +cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= +cloud.google.com/go/gkehub v0.14.3/go.mod h1:jAl6WafkHHW18qgq7kqcrXYzN08hXeK/Va3utN8VKg8= +cloud.google.com/go/gkehub v0.14.4/go.mod h1:Xispfu2MqnnFt8rV/2/3o73SK1snL8s9dYJ9G2oQMfc= +cloud.google.com/go/gkehub v0.14.5/go.mod h1:6bzqxM+a+vEH/h8W8ec4OJl4r36laxTs3A/fMNHJ0wA= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= +cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= +cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= +cloud.google.com/go/gkemulticloud v1.0.2/go.mod h1:+ee5VXxKb3H1l4LZAcgWB/rvI16VTNTrInWxDjAGsGo= +cloud.google.com/go/gkemulticloud v1.0.3/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= +cloud.google.com/go/gkemulticloud v1.1.0/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= +cloud.google.com/go/gkemulticloud v1.1.1/go.mod h1:C+a4vcHlWeEIf45IB5FFR5XGjTeYhF83+AYIpTy4i2Q= cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= +cloud.google.com/go/grafeas v0.3.4/go.mod h1:A5m316hcG+AulafjAbPKXBO/+I5itU4LOdKO2R/uDIc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= +cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= +cloud.google.com/go/gsuiteaddons v1.6.3/go.mod h1:sCFJkZoMrLZT3JTb8uJqgKPNshH2tfXeCwTFRebTq48= +cloud.google.com/go/gsuiteaddons v1.6.4/go.mod h1:rxtstw7Fx22uLOXBpsvb9DUbC+fiXs7rF4U29KHM/pE= +cloud.google.com/go/gsuiteaddons v1.6.5/go.mod h1:Lo4P2IvO8uZ9W+RaC6s1JVxo42vgy+TX5a6hfBZ0ubs= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= +cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= +cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= +cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= +cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= +cloud.google.com/go/iap v1.9.2/go.mod h1:GwDTOs047PPSnwRD0Us5FKf4WDRcVvHg1q9WVkKBhdI= +cloud.google.com/go/iap v1.9.3/go.mod h1:DTdutSZBqkkOm2HEOTBzhZxh2mwwxshfD/h3yofAiCw= +cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= +cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= +cloud.google.com/go/ids v1.4.3/go.mod h1:9CXPqI3GedjmkjbMWCUhMZ2P2N7TUMzAkVXYEH2orYU= +cloud.google.com/go/ids v1.4.4/go.mod h1:z+WUc2eEl6S/1aZWzwtVNWoSZslgzPxAboS0lZX0HjI= +cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= +cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= +cloud.google.com/go/iot v1.7.3/go.mod h1:t8itFchkol4VgNbHnIq9lXoOOtHNR3uAACQMYbN9N4I= +cloud.google.com/go/iot v1.7.4/go.mod h1:3TWqDVvsddYBG++nHSZmluoCAVGr1hAcabbWZNKEZLk= +cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= +cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= +cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= +cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= +cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= +cloud.google.com/go/kms v1.15.4/go.mod h1:L3Sdj6QTHK8dfwK5D1JLsAyELsNMnd3tAIwGS4ltKpc= +cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= +cloud.google.com/go/kms v1.15.6/go.mod h1:yF75jttnIdHfGBoE51AKsD/Yqf+/jICzB9v1s1acsms= +cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= +cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= +cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= +cloud.google.com/go/language v1.12.1/go.mod h1:zQhalE2QlQIxbKIZt54IASBzmZpN/aDASea5zl1l+J4= +cloud.google.com/go/language v1.12.2/go.mod h1:9idWapzr/JKXBBQ4lWqVX/hcadxB194ry20m/bTrhWc= +cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= +cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= +cloud.google.com/go/lifesciences v0.9.3/go.mod h1:gNGBOJV80IWZdkd+xz4GQj4mbqaz737SCLHn2aRhQKM= +cloud.google.com/go/lifesciences v0.9.4/go.mod h1:bhm64duKhMi7s9jR9WYJYvjAFJwRqNj+Nia7hF0Z7JA= +cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= +cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= +cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= +cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= +cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= +cloud.google.com/go/longrunning v0.5.3/go.mod h1:y/0ga59EYu58J6SHmmQOvekvND2qODbu8ywBBW7EK7Y= +cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= +cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= +cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= +cloud.google.com/go/managedidentities v1.6.3/go.mod h1:tewiat9WLyFN0Fi7q1fDD5+0N4VUoL0SCX0OTCthZq4= +cloud.google.com/go/managedidentities v1.6.4/go.mod h1:WgyaECfHmF00t/1Uk8Oun3CQ2PGUtjc3e9Alh79wyiM= +cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= +cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= +cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= +cloud.google.com/go/maps v1.5.1/go.mod h1:NPMZw1LJwQZYCfz4y+EIw+SI+24A4bpdFJqdKVr0lt4= +cloud.google.com/go/maps v1.6.1/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= +cloud.google.com/go/maps v1.6.2/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= +cloud.google.com/go/maps v1.6.3/go.mod h1:VGAn809ADswi1ASofL5lveOHPnE6Rk/SFTTBx1yuOLw= +cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= +cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= +cloud.google.com/go/mediatranslation v0.8.3/go.mod h1:F9OnXTy336rteOEywtY7FOqCk+J43o2RF638hkOQl4Y= +cloud.google.com/go/mediatranslation v0.8.4/go.mod h1:9WstgtNVAdN53m6TQa5GjIjLqKQPXe74hwSCxUP6nj4= +cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= +cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= +cloud.google.com/go/memcache v1.10.3/go.mod h1:6z89A41MT2DVAW0P4iIRdu5cmRTsbsFn4cyiIx8gbwo= +cloud.google.com/go/memcache v1.10.4/go.mod h1:v/d8PuC8d1gD6Yn5+I3INzLR01IDn0N4Ym56RgikSI0= +cloud.google.com/go/memcache v1.10.5/go.mod h1:/FcblbNd0FdMsx4natdj+2GWzTq+cjZvMa1I+9QsuMA= cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= +cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= +cloud.google.com/go/metastore v1.13.0/go.mod h1:URDhpG6XLeh5K+Glq0NOt74OfrPKTwS62gEPZzb5SOk= +cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= +cloud.google.com/go/metastore v1.13.2/go.mod h1:KS59dD+unBji/kFebVp8XU/quNSyo8b6N6tPGspKszA= +cloud.google.com/go/metastore v1.13.3/go.mod h1:K+wdjXdtkdk7AQg4+sXS8bRrQa9gcOr+foOMF2tqINE= +cloud.google.com/go/metastore v1.13.4/go.mod h1:FMv9bvPInEfX9Ac1cVcRXp8EBBQnBcqH6gz3KvJ9BAE= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= +cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= +cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= +cloud.google.com/go/monitoring v1.16.2/go.mod h1:B44KGwi4ZCF8Rk/5n+FWeispDXoKSk9oss2QNlXJBgc= +cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= +cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= +cloud.google.com/go/monitoring v1.17.1/go.mod h1:SJzPMakCF0GHOuKEH/r4hxVKF04zl+cRPQyc3d/fqII= +cloud.google.com/go/monitoring v1.18.0/go.mod h1:c92vVBCeq/OB4Ioyo+NbN2U7tlg5ZH41PZcdvfc+Lcg= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= +cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= +cloud.google.com/go/networkconnectivity v1.14.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= +cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= +cloud.google.com/go/networkconnectivity v1.14.2/go.mod h1:5UFlwIisZylSkGG1AdwK/WZUaoz12PKu6wODwIbFzJo= +cloud.google.com/go/networkconnectivity v1.14.3/go.mod h1:4aoeFdrJpYEXNvrnfyD5kIzs8YtHg945Og4koAjHQek= +cloud.google.com/go/networkconnectivity v1.14.4/go.mod h1:PU12q++/IMnDJAB+3r+tJtuCXCfwfN+C6Niyj6ji1Po= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= +cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= +cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= +cloud.google.com/go/networkmanagement v1.9.2/go.mod h1:iDGvGzAoYRghhp4j2Cji7sF899GnfGQcQRQwgVOWnDw= +cloud.google.com/go/networkmanagement v1.9.3/go.mod h1:y7WMO1bRLaP5h3Obm4tey+NquUvB93Co1oh4wpL+XcU= +cloud.google.com/go/networkmanagement v1.9.4/go.mod h1:daWJAl0KTFytFL7ar33I6R/oNBH8eEOX/rBNHrC/8TA= cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= +cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= +cloud.google.com/go/networksecurity v0.9.3/go.mod h1:l+C0ynM6P+KV9YjOnx+kk5IZqMSLccdBqW6GUoF4p/0= +cloud.google.com/go/networksecurity v0.9.4/go.mod h1:E9CeMZ2zDsNBkr8axKSYm8XyTqNhiCHf1JO/Vb8mD1w= +cloud.google.com/go/networksecurity v0.9.5/go.mod h1:KNkjH/RsylSGyyZ8wXpue8xpCEK+bTtvof8SBfIhMG8= cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= +cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= +cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= +cloud.google.com/go/notebooks v1.11.1/go.mod h1:V2Zkv8wX9kDCGRJqYoI+bQAaoVeE5kSiz4yYHd2yJwQ= +cloud.google.com/go/notebooks v1.11.2/go.mod h1:z0tlHI/lREXC8BS2mIsUeR3agM1AkgLiS+Isov3SS70= +cloud.google.com/go/notebooks v1.11.3/go.mod h1:0wQyI2dQC3AZyQqWnRsp+yA+kY4gC7ZIVP4Qg3AQcgo= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= +cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= +cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= +cloud.google.com/go/optimization v1.6.1/go.mod h1:hH2RYPTTM9e9zOiTaYPTiGPcGdNZVnBSBxjIAJzUkqo= +cloud.google.com/go/optimization v1.6.2/go.mod h1:mWNZ7B9/EyMCcwNl1frUGEuY6CPijSkz88Fz2vwKPOY= +cloud.google.com/go/optimization v1.6.3/go.mod h1:8ve3svp3W6NFcAEFr4SfJxrldzhUl4VMUJmhrqVKtYA= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= +cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= +cloud.google.com/go/orchestration v1.8.3/go.mod h1:xhgWAYqlbYjlz2ftbFghdyqENYW+JXuhBx9KsjMoGHs= +cloud.google.com/go/orchestration v1.8.4/go.mod h1:d0lywZSVYtIoSZXb0iFjv9SaL13PGyVOKDxqGxEf/qI= +cloud.google.com/go/orchestration v1.8.5/go.mod h1:C1J7HesE96Ba8/hZ71ISTV2UAat0bwN+pi85ky38Yq8= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= +cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= +cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= +cloud.google.com/go/orgpolicy v1.11.3/go.mod h1:oKAtJ/gkMjum5icv2aujkP4CxROxPXsBbYGCDbPO8MM= +cloud.google.com/go/orgpolicy v1.11.4/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= +cloud.google.com/go/orgpolicy v1.12.0/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= +cloud.google.com/go/orgpolicy v1.12.1/go.mod h1:aibX78RDl5pcK3jA8ysDQCFkVxLj3aOQqrbBaUL2V5I= cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= +cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= +cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= +cloud.google.com/go/osconfig v1.12.3/go.mod h1:L/fPS8LL6bEYUi1au832WtMnPeQNT94Zo3FwwV1/xGM= +cloud.google.com/go/osconfig v1.12.4/go.mod h1:B1qEwJ/jzqSRslvdOCI8Kdnp0gSng0xW4LOnIebQomA= +cloud.google.com/go/osconfig v1.12.5/go.mod h1:D9QFdxzfjgw3h/+ZaAb5NypM8bhOMqBzgmbhzWViiW8= cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= +cloud.google.com/go/oslogin v1.11.0/go.mod h1:8GMTJs4X2nOAUVJiPGqIWVcDaF0eniEto3xlOxaboXE= +cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= +cloud.google.com/go/oslogin v1.12.1/go.mod h1:VfwTeFJGbnakxAY236eN8fsnglLiVXndlbcNomY4iZU= +cloud.google.com/go/oslogin v1.12.2/go.mod h1:CQ3V8Jvw4Qo4WRhNPF0o+HAM4DiLuE27Ul9CX9g2QdY= +cloud.google.com/go/oslogin v1.13.0/go.mod h1:xPJqLwpTZ90LSE5IL1/svko+6c5avZLluiyylMb/sRA= +cloud.google.com/go/oslogin v1.13.1/go.mod h1:vS8Sr/jR7QvPWpCjNqy6LYZr5Zs1e8ZGW/KPn9gmhws= cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= +cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= +cloud.google.com/go/phishingprotection v0.8.3/go.mod h1:3B01yO7T2Ra/TMojifn8EoGd4G9jts/6cIO0DgDY9J8= +cloud.google.com/go/phishingprotection v0.8.4/go.mod h1:6b3kNPAc2AQ6jZfFHioZKg9MQNybDg4ixFd4RPZZ2nE= +cloud.google.com/go/phishingprotection v0.8.5/go.mod h1:g1smd68F7mF1hgQPuYn3z8HDbNre8L6Z0b7XMYFmX7I= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= +cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= +cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= +cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= +cloud.google.com/go/policytroubleshooter v1.10.1/go.mod h1:5C0rhT3TDZVxAu8813bwmTvd57Phbl8mr9F4ipOsxEs= +cloud.google.com/go/policytroubleshooter v1.10.2/go.mod h1:m4uF3f6LseVEnMV6nknlN2vYGRb+75ylQwJdnOXfnv0= +cloud.google.com/go/policytroubleshooter v1.10.3/go.mod h1:+ZqG3agHT7WPb4EBIRqUv4OyIwRTZvsVDHZ8GlZaoxk= cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= +cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= +cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= +cloud.google.com/go/privatecatalog v0.9.3/go.mod h1:K5pn2GrVmOPjXz3T26mzwXLcKivfIJ9R5N79AFCF9UE= +cloud.google.com/go/privatecatalog v0.9.4/go.mod h1:SOjm93f+5hp/U3PqMZAHTtBtluqLygrDrVO8X8tYtG0= +cloud.google.com/go/privatecatalog v0.9.5/go.mod h1:fVWeBOVe7uj2n3kWRGlUQqR/pOd450J9yZoOECcQqJk= 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/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= +cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= +cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= +cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= +cloud.google.com/go/recaptchaenterprise/v2 v2.8.0/go.mod h1:QuE8EdU9dEnesG8/kG3XuJyNsjEqMlMzg3v3scCJ46c= +cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= +cloud.google.com/go/recaptchaenterprise/v2 v2.8.2/go.mod h1:kpaDBOpkwD4G0GVMzG1W6Doy1tFFC97XAV3xy+Rd/pw= +cloud.google.com/go/recaptchaenterprise/v2 v2.8.3/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= +cloud.google.com/go/recaptchaenterprise/v2 v2.8.4/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= +cloud.google.com/go/recaptchaenterprise/v2 v2.9.0/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= +cloud.google.com/go/recaptchaenterprise/v2 v2.9.2/go.mod h1:trwwGkfhCmp05Ll5MSJPXY7yvnO0p4v3orGANAFHAuU= cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= +cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= +cloud.google.com/go/recommendationengine v0.8.3/go.mod h1:m3b0RZV02BnODE9FeSvGv1qibFo8g0OnmB/RMwYy4V8= +cloud.google.com/go/recommendationengine v0.8.4/go.mod h1:GEteCf1PATl5v5ZsQ60sTClUE0phbWmo3rQ1Js8louU= +cloud.google.com/go/recommendationengine v0.8.5/go.mod h1:A38rIXHGFvoPvmy6pZLozr0g59NRNREz4cx7F58HAsQ= cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= +cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= +cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= +cloud.google.com/go/recommender v1.11.2/go.mod h1:AeoJuzOvFR/emIcXdVFkspVXVTYpliRCmKNYDnyBv6Y= +cloud.google.com/go/recommender v1.11.3/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= +cloud.google.com/go/recommender v1.12.0/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= +cloud.google.com/go/recommender v1.12.1/go.mod h1:gf95SInWNND5aPas3yjwl0I572dtudMhMIG4ni8nr+0= cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= +cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= +cloud.google.com/go/redis v1.13.3/go.mod h1:vbUpCKUAZSYzFcWKmICnYgRAhTFg9r+djWqFxDYXi4U= +cloud.google.com/go/redis v1.14.1/go.mod h1:MbmBxN8bEnQI4doZPC1BzADU4HGocHBk2de3SbgOkqs= +cloud.google.com/go/redis v1.14.2/go.mod h1:g0Lu7RRRz46ENdFKQ2EcQZBAJ2PtJHJLuiiRuEXwyQw= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= +cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= +cloud.google.com/go/resourcemanager v1.9.3/go.mod h1:IqrY+g0ZgLsihcfcmqSe+RKp1hzjXwG904B92AwBz6U= +cloud.google.com/go/resourcemanager v1.9.4/go.mod h1:N1dhP9RFvo3lUfwtfLWVxfUWq8+KUQ+XLlHLH3BoFJ0= +cloud.google.com/go/resourcemanager v1.9.5/go.mod h1:hep6KjelHA+ToEjOfO3garMKi/CLYwTqeAw7YiEI9x8= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= +cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= +cloud.google.com/go/resourcesettings v1.6.3/go.mod h1:pno5D+7oDYkMWZ5BpPsb4SO0ewg3IXcmmrUZaMJrFic= +cloud.google.com/go/resourcesettings v1.6.4/go.mod h1:pYTTkWdv2lmQcjsthbZLNBP4QW140cs7wqA3DuqErVI= +cloud.google.com/go/resourcesettings v1.6.5/go.mod h1:WBOIWZraXZOGAgoR4ukNj0o0HiSMO62H9RpFi9WjP9I= cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= +cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= +cloud.google.com/go/retail v1.14.3/go.mod h1:Omz2akDHeSlfCq8ArPKiBxlnRpKEBjUH386JYFLUvXo= +cloud.google.com/go/retail v1.14.4/go.mod h1:l/N7cMtY78yRnJqp5JW8emy7MB1nz8E4t2yfOmklYfg= +cloud.google.com/go/retail v1.15.1/go.mod h1:In9nSBOYhLbDGa87QvWlnE1XA14xBN2FpQRiRsUs9wU= +cloud.google.com/go/retail v1.16.0/go.mod h1:LW7tllVveZo4ReWt68VnldZFWJRzsh9np+01J9dYWzE= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= +cloud.google.com/go/run v1.3.0/go.mod h1:S/osX/4jIPZGg+ssuqh6GNgg7syixKe3YnprwehzHKU= +cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= +cloud.google.com/go/run v1.3.2/go.mod h1:SIhmqArbjdU/D9M6JoHaAqnAMKLFtXaVdNeq04NjnVE= +cloud.google.com/go/run v1.3.3/go.mod h1:WSM5pGyJ7cfYyYbONVQBN4buz42zFqwG67Q3ch07iK4= +cloud.google.com/go/run v1.3.4/go.mod h1:FGieuZvQ3tj1e9GnzXqrMABSuir38AJg5xhiYq+SF3o= cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= +cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= +cloud.google.com/go/scheduler v1.10.3/go.mod h1:8ANskEM33+sIbpJ+R4xRfw/jzOG+ZFE8WVLy7/yGvbc= +cloud.google.com/go/scheduler v1.10.4/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= +cloud.google.com/go/scheduler v1.10.5/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= +cloud.google.com/go/scheduler v1.10.6/go.mod h1:pe2pNCtJ+R01E06XCDOJs1XvAMbv28ZsQEbqknxGOuE= cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= +cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= +cloud.google.com/go/secretmanager v1.11.3/go.mod h1:0bA2o6FabmShrEy328i67aV+65XoUFFSmVeLBn/51jI= +cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= +cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4= cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= +cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= +cloud.google.com/go/security v1.15.3/go.mod h1:gQ/7Q2JYUZZgOzqKtw9McShH+MjNvtDpL40J1cT+vBs= +cloud.google.com/go/security v1.15.4/go.mod h1:oN7C2uIZKhxCLiAAijKUCuHLZbIt/ghYEo8MqwD/Ty4= +cloud.google.com/go/security v1.15.5/go.mod h1:KS6X2eG3ynWjqcIX976fuToN5juVkF6Ra6c7MPnldtc= cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= +cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= +cloud.google.com/go/securitycenter v1.24.1/go.mod h1:3h9IdjjHhVMXdQnmqzVnM7b0wMn/1O/U20eWVpMpZjI= +cloud.google.com/go/securitycenter v1.24.2/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= +cloud.google.com/go/securitycenter v1.24.3/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= +cloud.google.com/go/securitycenter v1.24.4/go.mod h1:PSccin+o1EMYKcFQzz9HMMnZ2r9+7jbc+LvPjXhpwcU= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= +cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= +cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= +cloud.google.com/go/servicedirectory v1.11.2/go.mod h1:KD9hCLhncWRV5jJphwIpugKwM5bn1x0GyVVD4NO8mGg= +cloud.google.com/go/servicedirectory v1.11.3/go.mod h1:LV+cHkomRLr67YoQy3Xq2tUXBGOs5z5bPofdq7qtiAw= +cloud.google.com/go/servicedirectory v1.11.4/go.mod h1:Bz2T9t+/Ehg6x+Y7Ycq5xiShYLD96NfEsWNHyitj1qM= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= +cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= +cloud.google.com/go/shell v1.7.3/go.mod h1:cTTEz/JdaBsQAeTQ3B6HHldZudFoYBOqjteev07FbIc= +cloud.google.com/go/shell v1.7.4/go.mod h1:yLeXB8eKLxw0dpEmXQ/FjriYrBijNsONpwnWsdPqlKM= +cloud.google.com/go/shell v1.7.5/go.mod h1:hL2++7F47/IfpfTO53KYf1EC+F56k3ThfNEXd4zcuiE= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= +cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= +cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= +cloud.google.com/go/spanner v1.51.0/go.mod h1:c5KNo5LQ1X5tJwma9rSQZsXNBDNvj4/n8BVc3LNahq0= +cloud.google.com/go/spanner v1.53.0/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= +cloud.google.com/go/spanner v1.53.1/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= +cloud.google.com/go/spanner v1.54.0/go.mod h1:wZvSQVBgngF0Gq86fKup6KIYmN2be7uOKjtK97X+bQU= +cloud.google.com/go/spanner v1.55.0/go.mod h1:HXEznMUVhC+PC+HDyo9YFG2Ajj5BQDkcbqB9Z2Ffxi0= +cloud.google.com/go/spanner v1.56.0/go.mod h1:DndqtUKQAt3VLuV2Le+9Y3WTnq5cNKrnLb/Piqcj+h0= +cloud.google.com/go/spanner v1.57.0/go.mod h1:aXQ5QDdhPRIqVhYmnkAdwPYvj/DRN0FguclhEWw+jOo= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= +cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= +cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= +cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= +cloud.google.com/go/speech v1.19.2/go.mod h1:2OYFfj+Ch5LWjsaSINuCZsre/789zlcCI3SY4oAi2oI= +cloud.google.com/go/speech v1.20.1/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= +cloud.google.com/go/speech v1.21.0/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= +cloud.google.com/go/speech v1.21.1/go.mod h1:E5GHZXYQlkqWQwY5xRSLHw2ci5NMQNG52FfMU1aZrIA= 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.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.37.0/go.mod h1:i34TiT2IhiNDmcj65PqwCjcoUX7Z5pLzS8DEmoiFq1k= cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= +cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= +cloud.google.com/go/storagetransfer v1.10.2/go.mod h1:meIhYQup5rg9juQJdyppnA/WLQCOguxtk1pr3/vBWzA= +cloud.google.com/go/storagetransfer v1.10.3/go.mod h1:Up8LY2p6X68SZ+WToswpQbQHnJpOty/ACcMafuey8gc= +cloud.google.com/go/storagetransfer v1.10.4/go.mod h1:vef30rZKu5HSEf/x1tK3WfWrL0XVoUQN/EPDRGPzjZs= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= +cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= +cloud.google.com/go/talent v1.6.4/go.mod h1:QsWvi5eKeh6gG2DlBkpMaFYZYrYUnIpo34f6/V5QykY= +cloud.google.com/go/talent v1.6.5/go.mod h1:Mf5cma696HmE+P2BWJ/ZwYqeJXEeU0UqjHFXVLadEDI= +cloud.google.com/go/talent v1.6.6/go.mod h1:y/WQDKrhVz12WagoarpAIyKKMeKGKHWPoReZ0g8tseQ= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= +cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= +cloud.google.com/go/texttospeech v1.7.3/go.mod h1:Av/zpkcgWfXlDLRYob17lqMstGZ3GqlvJXqKMp2u8so= +cloud.google.com/go/texttospeech v1.7.4/go.mod h1:vgv0002WvR4liGuSd5BJbWy4nDn5Ozco0uJymY5+U74= +cloud.google.com/go/texttospeech v1.7.5/go.mod h1:tzpCuNWPwrNJnEa4Pu5taALuZL4QRRLcb+K9pbhXT6M= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= +cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= +cloud.google.com/go/tpu v1.6.3/go.mod h1:lxiueqfVMlSToZY1151IaZqp89ELPSrk+3HIQ5HRkbY= +cloud.google.com/go/tpu v1.6.4/go.mod h1:NAm9q3Rq2wIlGnOhpYICNI7+bpBebMJbh0yyp3aNw1Y= +cloud.google.com/go/tpu v1.6.5/go.mod h1:P9DFOEBIBhuEcZhXi+wPoVy/cji+0ICFi4TtTkMHSSs= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= +cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= +cloud.google.com/go/trace v1.10.3/go.mod h1:Ke1bgfc73RV3wUFml+uQp7EsDw4dGaETLxB7Iq/r4CY= +cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= +cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= +cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= +cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= +cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= +cloud.google.com/go/translate v1.9.2/go.mod h1:E3Tc6rUTsQkVrXW6avbUhKJSr7ZE3j7zNmqzXKHqRrY= +cloud.google.com/go/translate v1.9.3/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= +cloud.google.com/go/translate v1.10.0/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= +cloud.google.com/go/translate v1.10.1/go.mod h1:adGZcQNom/3ogU65N9UXHOnnSvjPwA/jKQUMnsYXOyk= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= +cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= +cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= +cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= +cloud.google.com/go/video v1.20.2/go.mod h1:lrixr5JeKNThsgfM9gqtwb6Okuqzfo4VrY2xynaViTA= +cloud.google.com/go/video v1.20.3/go.mod h1:TnH/mNZKVHeNtpamsSPygSR0iHtvrR/cW1/GDjN5+GU= +cloud.google.com/go/video v1.20.4/go.mod h1:LyUVjyW+Bwj7dh3UJnUGZfyqjEto9DnrvTe1f/+QrW0= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= +cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= +cloud.google.com/go/videointelligence v1.11.3/go.mod h1:tf0NUaGTjU1iS2KEkGWvO5hRHeCkFK3nPo0/cOZhZAo= +cloud.google.com/go/videointelligence v1.11.4/go.mod h1:kPBMAYsTPFiQxMLmmjpcZUMklJp3nC9+ipJJtprccD8= +cloud.google.com/go/videointelligence v1.11.5/go.mod h1:/PkeQjpRponmOerPeJxNPuxvi12HlW7Em0lJO14FC3I= cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= +cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= +cloud.google.com/go/vision/v2 v2.7.4/go.mod h1:ynDKnsDN/0RtqkKxQZ2iatv3Dm9O+HfRb5djl7l4Vvw= +cloud.google.com/go/vision/v2 v2.7.5/go.mod h1:GcviprJLFfK9OLf0z8Gm6lQb6ZFUulvpZws+mm6yPLM= +cloud.google.com/go/vision/v2 v2.7.6/go.mod h1:ZkvWTVNPBU3YZYzgF9Y1jwEbD1NBOCyJn0KFdQfE6Bw= +cloud.google.com/go/vision/v2 v2.8.0/go.mod h1:ocqDiA2j97pvgogdyhoxiQp2ZkDCyr0HWpicywGGRhU= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= +cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= +cloud.google.com/go/vmmigration v1.7.3/go.mod h1:ZCQC7cENwmSWlwyTrZcWivchn78YnFniEQYRWQ65tBo= +cloud.google.com/go/vmmigration v1.7.4/go.mod h1:yBXCmiLaB99hEl/G9ZooNx2GyzgsjKnw5fWcINRgD70= +cloud.google.com/go/vmmigration v1.7.5/go.mod h1:pkvO6huVnVWzkFioxSghZxIGcsstDvYiVCxQ9ZH3eYI= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= +cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= +cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= +cloud.google.com/go/vmwareengine v1.0.2/go.mod h1:xMSNjIk8/itYrz1JA8nV3Ajg4L4n3N+ugP8JKzk3OaA= +cloud.google.com/go/vmwareengine v1.0.3/go.mod h1:QSpdZ1stlbfKtyt6Iu19M6XRxjmXO+vb5a/R6Fvy2y4= +cloud.google.com/go/vmwareengine v1.1.1/go.mod h1:nMpdsIVkUrSaX8UvmnBhzVzG7PPvNYc5BszcvIVudYs= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= +cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= +cloud.google.com/go/vpcaccess v1.7.3/go.mod h1:YX4skyfW3NC8vI3Fk+EegJnlYFatA+dXK4o236EUCUc= +cloud.google.com/go/vpcaccess v1.7.4/go.mod h1:lA0KTvhtEOb/VOdnH/gwPuOzGgM+CWsmGu6bb4IoMKk= +cloud.google.com/go/vpcaccess v1.7.5/go.mod h1:slc5ZRvvjP78c2dnL7m4l4R9GwL3wDLcpIWz6P/ziig= cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= +cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= +cloud.google.com/go/webrisk v1.9.3/go.mod h1:RUYXe9X/wBDXhVilss7EDLW9ZNa06aowPuinUOPCXH8= +cloud.google.com/go/webrisk v1.9.4/go.mod h1:w7m4Ib4C+OseSr2GL66m0zMBywdrVNTDKsdEsfMl7X0= +cloud.google.com/go/webrisk v1.9.5/go.mod h1:aako0Fzep1Q714cPEM5E+mtYX8/jsfegAuS8aivxy3U= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= +cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= +cloud.google.com/go/websecurityscanner v1.6.3/go.mod h1:x9XANObUFR+83Cya3g/B9M/yoHVqzxPnFtgF8yYGAXw= +cloud.google.com/go/websecurityscanner v1.6.4/go.mod h1:mUiyMQ+dGpPPRkHgknIZeCzSHJ45+fY4F52nZFDHm2o= +cloud.google.com/go/websecurityscanner v1.6.5/go.mod h1:QR+DWaxAz2pWooylsBF854/Ijvuoa3FCyS1zBa1rAVQ= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= +cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= +cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= +cloud.google.com/go/workflows v1.12.2/go.mod h1:+OmBIgNqYJPVggnMo9nqmizW0qEXHhmnAzK/CnBqsHc= +cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= +cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= cosmossdk.io/client/v2 v2.0.0-beta.1 h1:XkHh1lhrLYIT9zKl7cIOXUXg2hdhtjTPBUfqERNA1/Q= @@ -217,10 +1347,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= -github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= -github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= -github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -235,6 +1363,7 @@ github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dX github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= @@ -243,7 +1372,6 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= @@ -259,17 +1387,34 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSi github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y= +github.com/alecthomas/participle/v2 v2.1.0/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 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/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= +github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= +github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw= +github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybFg8QBQ5LU+eBY= 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/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -304,6 +1449,8 @@ github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsy github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= @@ -358,33 +1505,40 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/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/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= 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.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= 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 v0.0.0-20210722231415-061457976a23/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-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 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-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= @@ -451,6 +1605,8 @@ github.com/cosmos/ibc-go/v8 v8.4.0 h1:K2PfX0AZ+1XKZytHGEMuSjQXG/MZshPb83RSTQt2+c github.com/cosmos/ibc-go/v8 v8.4.0/go.mod h1:zh6x1osR0hNvEcFrC/lhGD08sMfQmr9wHVvZ/mRWMCs= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= +github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= github.com/cosmos/relayer/v2 v2.5.2 h1:AF0MOo1GvJo94QNB996fBHdKlH+vrIY3JcFNrIvZNP0= @@ -465,6 +1621,7 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -499,12 +1656,15 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/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 v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -513,22 +1673,26 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/emicklei/dot v1.6.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.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.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -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.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= @@ -536,6 +1700,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fergusstrange/embedded-postgres v1.10.0 h1:YnwF6xAQYmKLAXXrrRx4rHDLih47YJwVPvg8jeKfdNg= github.com/fergusstrange/embedded-postgres v1.10.0/go.mod h1:a008U8/Rws5FtIOTGYDYa7beVWsT3qVKyqExqYYjL+c= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 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= @@ -561,6 +1727,11 @@ github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 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-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= 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= @@ -572,18 +1743,24 @@ github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4F 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-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/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.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -592,6 +1769,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -607,8 +1785,11 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= +github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= 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= @@ -617,26 +1798,24 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= 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-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.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= @@ -664,6 +1843,7 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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.3/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= @@ -676,8 +1856,9 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z 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 v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= -github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg= +github.com/google/flatbuffers v23.5.26+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= @@ -695,6 +1876,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= +github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= 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= @@ -717,15 +1900,20 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf 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-20201218002935-b9804c9f04c2/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-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= +github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= @@ -734,11 +1922,18 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3 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.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0/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.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -750,9 +1945,16 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= @@ -775,6 +1977,10 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf 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.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= @@ -827,6 +2033,7 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= @@ -837,10 +2044,12 @@ github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0Jr github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 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/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= 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= @@ -935,31 +2144,42 @@ github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b h1:FQ7+9fxhyp82ks9vAuy github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b/go.mod h1:HMcgvsgd0Fjj4XXDkbjdmlbI505rUPBs6WBMYg2pXks= 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/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/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.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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= @@ -1012,7 +2232,10 @@ github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9i github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -1020,6 +2243,7 @@ github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GW github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -1031,6 +2255,7 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd 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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -1038,6 +2263,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -1046,6 +2273,8 @@ github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXd github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -1151,11 +2380,15 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= 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/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.8 h1:ieHkV+i2BRzngO4Wd/3HGowuZStgq6QkPsD1eolNAO4= -github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1164,6 +2397,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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= @@ -1182,6 +2417,9 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1205,13 +2443,17 @@ github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -1226,6 +2468,8 @@ github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -1259,7 +2503,10 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= @@ -1272,6 +2519,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/strangelove-ventures/cometbft-client v0.1.0 h1:fcA652QaaR0LDnyJOZVjZKtuyAawnVXaq/p1MWJSYD4= github.com/strangelove-ventures/cometbft-client v0.1.0/go.mod h1:QzThgjzvsGgUNVNpGPitmxOWMIhp6a0oqf80nCRNt/0= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1294,11 +2542,14 @@ 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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= @@ -1356,12 +2607,16 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 h1:qxen9oVGzDdIRP6ejyAJc760RwW4SnVDiTYTzwnXuxo= @@ -1394,28 +2649,46 @@ 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/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1 h1:ofMbch7i29qIUf7VtF+r0HRF6ac0SBaPSziSsKp7wkk= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1 h1:CFMFNoz+CGprjFAFy+RJFrfEe4GBia3RRm2a4fREvCA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= 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= @@ -1451,16 +2724,36 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +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.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= 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= @@ -1468,11 +2761,24 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 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= @@ -1495,12 +2801,21 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +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.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1541,6 +2856,7 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/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= @@ -1548,6 +2864,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1558,9 +2877,25 @@ golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.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.7.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.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +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.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1586,8 +2921,18 @@ golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7Lm golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1603,11 +2948,17 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.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.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1624,6 +2975,7 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1663,7 +3015,9 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/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= @@ -1678,10 +3032,13 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/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-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1692,6 +3049,7 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc 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-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1699,14 +3057,27 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/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.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.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.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1714,7 +3085,20 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +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.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +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.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1728,20 +3112,28 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-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.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/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= @@ -1757,6 +3149,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/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-20190927191325-030b2cf1153e/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= @@ -1790,18 +3183,30 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc 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-20201124115921-2c860bdd6e78/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-20201224043029-2b0845dc783e/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.0.0-20210108195828-e2f9c7f1fc8e/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.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +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.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1816,6 +3221,15 @@ golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= 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= @@ -1864,10 +3278,31 @@ google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaE google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= +google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= +google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= +google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= +google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= +google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= +google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= +google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= +google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1875,6 +3310,7 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww 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/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 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= @@ -1897,6 +3333,7 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG 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= @@ -1913,8 +3350,10 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D 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-20210108203827-ffc7fda8c3d7/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-20210226172003-ab064af71705/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= @@ -1948,6 +3387,7 @@ google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= @@ -1980,57 +3420,130 @@ google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53B google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= +google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= +google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= +google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= +google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= +google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= +google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= +google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= +google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/api v0.0.0-20240122161410-6c6643bf1457/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= +google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20231212172506-995d672761c0/go.mod h1:guYXGPwC6jwxgWKW5Y405fKWOFNwlvUlUnzyp9i0uqo= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:ZSvZ8l+AWJwXw91DoTjWjaVLpWU6o0eZ4YLYpH8aLeQ= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:SCz6T5xjNXM4QFPRwxHcfChp7V+9DcXR3ay2TkHR8Tg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920183334-c177e329c48b/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240122161410-6c6643bf1457/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.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.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -2047,6 +3560,11 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -2086,7 +3604,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2094,32 +3611,79 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= +modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= +modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= +modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= +modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= +modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= +modernc.org/libc v1.21.2/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= +modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= +modernc.org/libc v1.22.4 h1:wymSbZb0AlrjdAVX3cjreCHTPCpPARbQXNz6BHPzdwQ= +modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= +modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs= -modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= +modernc.org/sqlite v1.21.2 h1:ixuUG0QS413Vfzyx6FWx6PYTmHaOegTY+hjzhn7L+a0= +modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34= -modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= +modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws= +modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= @@ -2127,6 +3691,7 @@ nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0 pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= diff --git a/internal/config/config.go b/internal/config/config.go index 68ca0b1..bf0529c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,6 +6,7 @@ import ( "strings" "time" + bbncfg "github.com/babylonlabs-io/babylon/client/config" queue "github.com/babylonlabs-io/staking-queue-client/config" "github.com/spf13/viper" ) @@ -84,9 +85,16 @@ func New(cfgFile string) (*Config, error) { } func DefaultConfig() *Config { + bbnCfg := bbncfg.DefaultBabylonConfig() cfg := &Config{ BTC: *DefaultBTCConfig(), Db: *DefaultDBConfig(), + BBN: BBNConfig{ + RPCAddr: bbnCfg.RPCAddr, + Timeout: bbnCfg.Timeout, + MaxRetryTimes: 3, + RetryInterval: 1 * time.Second, + }, Poller: PollerConfig{ ParamPollingInterval: 1 * time.Second, ExpiryCheckerPollingInterval: 1 * time.Second, From 33269f2f55c655bc05979d2ba95a1166f47014b8 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 15 Dec 2024 16:04:32 +0530 Subject: [PATCH 12/32] activate del --- e2etest/e2e_test.go | 5 +++++ e2etest/test_manager_btcstaking.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 301ebee..aab77f9 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -99,6 +99,9 @@ func TestActivatingDelegation(t *testing.T) { mBlock := tm.mineBlock(t) require.Equal(t, 2, len(mBlock.Transactions)) + // get spv proof of the BTC staking tx + stakingTxInfo := getTxInfo(t, mBlock) + // wait until staking tx is on Bitcoin require.Eventually(t, func() bool { _, err := tm.WalletClient.GetRawTransaction(&stakingMsgTxHash) @@ -126,6 +129,8 @@ func TestActivatingDelegation(t *testing.T) { wg.Wait() + tm.SubmitInclusionProof(t, stakingMsgTxHash.String(), stakingTxInfo) + // // make sure we didn't submit any "invalid" incl proof // require.Eventually(t, func() bool { // return promtestutil.ToFloat64(stakingTrackerMetrics.FailedReportedActivateDelegations) == 0 diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index f4c9453..783921b 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -917,3 +917,17 @@ func (tm *TestManager) getHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, } return &highUTXO, sum, nil } + +func (tm *TestManager) SubmitInclusionProof(t *testing.T, stakingTxHash string, txInfo *btcctypes.TransactionInfo) { + msg := &bstypes.MsgAddBTCDelegationInclusionProof{ + Signer: tm.MustGetBabylonSigner(), + StakingTxHash: stakingTxHash, + StakingTxInclusionProof: &bstypes.InclusionProof{ + Key: txInfo.Key, + Proof: txInfo.Proof, + }, + } + + _, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) + require.NoError(t, err) +} From 44e372867c4d1f222eeeb80eba2abf5e9e7192b5 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 15 Dec 2024 16:22:43 +0530 Subject: [PATCH 13/32] fix --- e2etest/e2e_test.go | 2 +- e2etest/test_manager.go | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index aab77f9..5bb119c 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -61,7 +61,7 @@ func TestQueueConsumer(t *testing.T) { // eventually become "active". // Specifically, that stakingEventWatcher will send a MsgAddBTCDelegationInclusionProof to do so. func TestActivatingDelegation(t *testing.T) { - t.Parallel() + // t.Parallel() // segwit is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index d90b114..516fad8 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -8,16 +8,20 @@ import ( "fmt" "os" "path/filepath" + "sync" "testing" "time" "github.com/babylonlabs-io/babylon-staking-indexer/e2etest/container" + indexerbbnclient "github.com/babylonlabs-io/babylon-staking-indexer/internal/clients/bbnclient" "github.com/babylonlabs-io/babylon-staking-indexer/internal/clients/btcclient" "github.com/babylonlabs-io/babylon-staking-indexer/internal/config" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/db" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/observability/metrics" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/services" _ "github.com/babylonlabs-io/babylon/app/params" bbnclient "github.com/babylonlabs-io/babylon/client/client" bbncfg "github.com/babylonlabs-io/babylon/client/config" - _ "github.com/babylonlabs-io/babylon/types" bbn "github.com/babylonlabs-io/babylon/types" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" "github.com/btcsuite/btcd/btcec/v2" @@ -131,7 +135,40 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u ) require.NoError(t, err) + ctx := context.Background() + dbClient, err := db.New(ctx, cfg.Db) + require.NoError(t, err) + + queueConsumer, err := setupTestQueueConsumer(t, &cfg.Queue) + require.NoError(t, err) + + btcNotifier, err := btcclient.NewBTCNotifier( + &cfg.BTC, + &btcclient.EmptyHintCache{}, + ) + require.NoError(t, err) + + cfg.BBN.RPCAddr = fmt.Sprintf("http://localhost:%s", babylond.GetPort("26657/tcp")) + bbnClient := indexerbbnclient.NewBBNClient(&cfg.BBN) + + service := services.NewService(cfg, dbClient, btcClient, btcNotifier, bbnClient, queueConsumer) + require.NoError(t, err) + + // initialize metrics with the metrics port from config + metricsPort := cfg.Metrics.GetMetricsPort() + metrics.Init(metricsPort) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + service.StartIndexerSync(ctx) + }() + // Wait for the server to start + time.Sleep(3 * time.Second) + return &TestManager{ + WalletClient: rpcclient, BabylonClient: babylonClient, BitcoindHandler: btcHandler, From b6d20b268962ba2ed40686dd87a9dd230233a8b7 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 15 Dec 2024 17:12:48 +0530 Subject: [PATCH 14/32] add more assertions --- e2etest/e2e_test.go | 6 ++++ e2etest/test_manager.go | 76 +++++++++++++++++++++++++++++++++-------- internal/config/db.go | 8 ++--- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 5bb119c..3c9c40d 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -146,4 +146,10 @@ func TestActivatingDelegation(t *testing.T) { return resp.BtcDelegation.Active }, eventuallyWaitTimeOut, eventuallyPollTime) + + // check that the staking tx is already stored + _ = tm.WaitForStakingTxStored(t, stakingMsgTxHash) + + // check that the staking event is already stored + tm.CheckNextStakingEvent(t, stakingMsgTxHash) } diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 516fad8..9c7f07d 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -17,6 +17,7 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/clients/btcclient" "github.com/babylonlabs-io/babylon-staking-indexer/internal/config" "github.com/babylonlabs-io/babylon-staking-indexer/internal/db" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" "github.com/babylonlabs-io/babylon-staking-indexer/internal/observability/metrics" "github.com/babylonlabs-io/babylon-staking-indexer/internal/services" _ "github.com/babylonlabs-io/babylon/app/params" @@ -24,6 +25,8 @@ import ( bbncfg "github.com/babylonlabs-io/babylon/client/config" bbn "github.com/babylonlabs-io/babylon/types" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + queuecli "github.com/babylonlabs-io/staking-queue-client/client" + "github.com/babylonlabs-io/staking-queue-client/queuemngr" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" @@ -47,13 +50,16 @@ var ( ) type TestManager struct { - BitcoindHandler *BitcoindTestHandler - BabylonClient *bbnclient.Client - BTCClient *btcclient.BTCClient - WalletClient *rpcclient.Client - WalletPrivKey *btcec.PrivateKey - Config *config.Config - manager *container.Manager + BitcoindHandler *BitcoindTestHandler + BabylonClient *bbnclient.Client + BTCClient *btcclient.BTCClient + WalletClient *rpcclient.Client + WalletPrivKey *btcec.PrivateKey + Config *config.Config + manager *container.Manager + DbClient *db.Database + QueueConsumer *queuemngr.QueueManager + ActiveStakingEventChan <-chan queuecli.QueueMessage } // StartManager creates a test manager @@ -158,6 +164,9 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u metricsPort := cfg.Metrics.GetMetricsPort() metrics.Init(metricsPort) + activeStakingEventChan, err := queueConsumer.ActiveStakingQueue.ReceiveMessages() + require.NoError(t, err) + var wg sync.WaitGroup wg.Add(1) go func() { @@ -168,14 +177,15 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u time.Sleep(3 * time.Second) return &TestManager{ - - WalletClient: rpcclient, - BabylonClient: babylonClient, - BitcoindHandler: btcHandler, - BTCClient: btcClient, - Config: cfg, - WalletPrivKey: walletPrivKey, - manager: manager, + WalletClient: rpcclient, + BabylonClient: babylonClient, + BitcoindHandler: btcHandler, + BTCClient: btcClient, + Config: cfg, + WalletPrivKey: walletPrivKey, + manager: manager, + ActiveStakingEventChan: activeStakingEventChan, + DbClient: dbClient, } } @@ -239,6 +249,8 @@ func DefaultStakingIndexerConfig() *config.Config { defaultConfig.BTC.BlockPollingInterval = 1 * time.Second defaultConfig.BTC.TxPollingInterval = 1 * time.Second + defaultConfig.Queue.QueueProcessingTimeout = time.Duration(500) * time.Second + return defaultConfig } @@ -327,3 +339,37 @@ func importPrivateKey(btcHandler *BitcoindTestHandler) (*btcec.PrivateKey, error return privKey, nil } + +func (tm *TestManager) WaitForStakingTxStored(t *testing.T, txHash chainhash.Hash) *model.BTCDelegationDetails { + var storedDelegation model.BTCDelegationDetails + require.Eventually(t, func() bool { + storedDelegation, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), txHash.String()) + if err != nil || storedDelegation == nil { + return false + } + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + + require.Equal(t, txHash.String(), storedDelegation.StakingTxHashHex) + + return &storedDelegation +} + +func (tm *TestManager) CheckNextStakingEvent(t *testing.T, stakingTxHash chainhash.Hash) { + stakingEventBytes := <-tm.ActiveStakingEventChan + var activeStakingEvent queuecli.StakingEvent + err := json.Unmarshal([]byte(stakingEventBytes.Body), &activeStakingEvent) + require.NoError(t, err) + + storedStakingTx, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), stakingTxHash.String()) + require.NotNil(t, storedStakingTx) + require.NoError(t, err) + require.Equal(t, stakingTxHash.String(), activeStakingEvent.StakingTxHashHex) + require.Equal(t, storedStakingTx.StakingTxHashHex, activeStakingEvent.StakingTxHashHex) + require.Equal(t, storedStakingTx.StakingAmount, activeStakingEvent.StakingAmount) + require.Equal(t, storedStakingTx.StakerBtcPkHex, activeStakingEvent.StakerBtcPkHex) + require.Equal(t, storedStakingTx.FinalityProviderBtcPksHex, activeStakingEvent.FinalityProviderBtcPksHex) + + err = tm.QueueConsumer.ActiveStakingQueue.DeleteMessage(stakingEventBytes.Receipt) + require.NoError(t, err) +} diff --git a/internal/config/db.go b/internal/config/db.go index 403f933..984b756 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -61,10 +61,10 @@ func (cfg *DbConfig) Validate() error { } const ( - defaultDbAddress = "mongodb://localhost:27017" - defaultDbUsername = "user" - defaultDbPassword = "pass" - defaultDbName = "staking" + defaultDbAddress = "mongodb://localhost:27019/?replicaSet=RS&directConnection=true" + defaultDbUsername = "root" + defaultDbPassword = "example" + defaultDbName = "babylon-staking-indexer" ) func DefaultDBConfig() *DbConfig { From 46fe8176e53f244c83ee9794fcefe0000fd14e14 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 15 Dec 2024 18:42:11 +0530 Subject: [PATCH 15/32] fix --- e2etest/e2e_test.go | 4 ++-- e2etest/test_manager.go | 23 +++++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 3c9c40d..8cfaa57 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -148,8 +148,8 @@ func TestActivatingDelegation(t *testing.T) { }, eventuallyWaitTimeOut, eventuallyPollTime) // check that the staking tx is already stored - _ = tm.WaitForStakingTxStored(t, stakingMsgTxHash) + _ = tm.WaitForStakingTxStored(t, stakingMsgTxHash.String()) // check that the staking event is already stored - tm.CheckNextStakingEvent(t, stakingMsgTxHash) + tm.CheckNextStakingEvent(t, stakingMsgTxHash.String()) } diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 9c7f07d..961c540 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -36,6 +36,7 @@ import ( "github.com/btcsuite/btcd/wire" pv "github.com/cosmos/relayer/v2/relayer/provider" "github.com/stretchr/testify/require" + "go.uber.org/zap" ) var ( @@ -145,9 +146,12 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u dbClient, err := db.New(ctx, cfg.Db) require.NoError(t, err) - queueConsumer, err := setupTestQueueConsumer(t, &cfg.Queue) + queueConsumer, err := queuemngr.NewQueueManager(&cfg.Queue, zap.NewNop()) require.NoError(t, err) + // queueConsumer, err := setupTestQueueConsumer(t, &cfg.Queue) + // require.NoError(t, err) + btcNotifier, err := btcclient.NewBTCNotifier( &cfg.BTC, &btcclient.EmptyHintCache{}, @@ -250,6 +254,7 @@ func DefaultStakingIndexerConfig() *config.Config { defaultConfig.BTC.TxPollingInterval = 1 * time.Second defaultConfig.Queue.QueueProcessingTimeout = time.Duration(500) * time.Second + defaultConfig.Queue.ReQueueDelayTime = time.Duration(300) * time.Second return defaultConfig } @@ -340,31 +345,33 @@ func importPrivateKey(btcHandler *BitcoindTestHandler) (*btcec.PrivateKey, error return privKey, nil } -func (tm *TestManager) WaitForStakingTxStored(t *testing.T, txHash chainhash.Hash) *model.BTCDelegationDetails { +func (tm *TestManager) WaitForStakingTxStored(t *testing.T, stakingTxHashHex string) *model.BTCDelegationDetails { var storedDelegation model.BTCDelegationDetails require.Eventually(t, func() bool { - storedDelegation, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), txHash.String()) - if err != nil || storedDelegation == nil { + x, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), stakingTxHashHex) + if err != nil || x == nil { return false } + + storedDelegation = *x return true }, eventuallyWaitTimeOut, eventuallyPollTime) - require.Equal(t, txHash.String(), storedDelegation.StakingTxHashHex) + require.Equal(t, stakingTxHashHex, storedDelegation.StakingTxHashHex) return &storedDelegation } -func (tm *TestManager) CheckNextStakingEvent(t *testing.T, stakingTxHash chainhash.Hash) { +func (tm *TestManager) CheckNextStakingEvent(t *testing.T, stakingTxHashHex string) { stakingEventBytes := <-tm.ActiveStakingEventChan var activeStakingEvent queuecli.StakingEvent err := json.Unmarshal([]byte(stakingEventBytes.Body), &activeStakingEvent) require.NoError(t, err) - storedStakingTx, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), stakingTxHash.String()) + storedStakingTx, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), stakingTxHashHex) require.NotNil(t, storedStakingTx) require.NoError(t, err) - require.Equal(t, stakingTxHash.String(), activeStakingEvent.StakingTxHashHex) + require.Equal(t, stakingTxHashHex, activeStakingEvent.StakingTxHashHex) require.Equal(t, storedStakingTx.StakingTxHashHex, activeStakingEvent.StakingTxHashHex) require.Equal(t, storedStakingTx.StakingAmount, activeStakingEvent.StakingAmount) require.Equal(t, storedStakingTx.StakerBtcPkHex, activeStakingEvent.StakerBtcPkHex) From b1b0fe82b9efb06ed8436bc0a7f3f456076c8839 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 11:11:33 +0530 Subject: [PATCH 16/32] queue consumer --- e2etest/test_manager.go | 1 + 1 file changed, 1 insertion(+) diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 961c540..d61e030 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -190,6 +190,7 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u manager: manager, ActiveStakingEventChan: activeStakingEventChan, DbClient: dbClient, + QueueConsumer: queueConsumer, } } From ae236ebee2e7562f70e23e8fe6d7d80e4c0fcb56 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 11:30:41 +0530 Subject: [PATCH 17/32] introduce make command --- .github/workflows/ci.yml | 18 ++++++++++-------- .github/workflows/publish.yml | 9 +++++---- Makefile | 6 +++++- e2etest/e2e_test.go | 3 +++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d08e60f..8716fd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,17 +7,19 @@ on: jobs: lint_test: - uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.6.0 + uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.7.0 with: - run-unit-tests: true - run-integration-tests: false - run-lint: false - run-build: true - run-gosec: true - gosec-args: "-no-fail ./..." + go-version: '1.23' + go-lint-version: 'v1.60.2' + run-unit-tests: true + run-integration-tests: true + run-lint: true + run-build: true + run-gosec: true + gosec-args: "-no-fail ./..." docker_pipeline: - uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.6.0 + uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.7.0 secrets: inherit with: publish: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e05ecb1..49b0290 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,15 +9,16 @@ on: jobs: lint_test: - uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.6.0 + uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.7.0 with: + go-version: '1.23' + go-lint-version: 'v1.60.2' run-unit-tests: true - run-integration-tests: false - run-lint: false + run-integration-tests: true docker_pipeline: needs: ["lint_test"] - uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.6.0 + uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.7.0 secrets: inherit with: publish: true diff --git a/Makefile b/Makefile index 34854ac..d2b8f15 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TOOLS_DIR := tools - +PACKAGES_E2E=$(shell go list ./... | grep '/e2etest') BUILDDIR ?= $(CURDIR)/build ldflags := $(LDFLAGS) @@ -50,3 +50,7 @@ generate-mock-interface: test: ./bin/local-startup.sh; go test -v -cover ./... + +test-e2e: + ./bin/local-startup.sh; + go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 8cfaa57..6030bce 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -1,3 +1,6 @@ +//go:build e2e +// +build e2e + package e2etest import ( From 3db9c31ede47cdcdc2305a7b25bd1591114cde0e Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 11:49:24 +0530 Subject: [PATCH 18/32] fix dead code --- e2etest/e2e_test.go | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 6030bce..d8670d4 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -1,6 +1,3 @@ -//go:build e2e -// +build e2e - package e2etest import ( @@ -64,26 +61,14 @@ func TestQueueConsumer(t *testing.T) { // eventually become "active". // Specifically, that stakingEventWatcher will send a MsgAddBTCDelegationInclusionProof to do so. func TestActivatingDelegation(t *testing.T) { - // t.Parallel() // segwit is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) tm := StartManager(t, numMatureOutputs, defaultEpochInterval) defer tm.Stop(t) + // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) - // - //btcNotifier, err := btcclient.NewNodeBackend( - // btcclient.ToBitcoindConfig(tm.Config.BTC), - // &chaincfg.RegressionNetParams, - // &btcclient.EmptyHintCache{}, - //) - //require.NoError(t, err) - // - //err = btcNotifier.Start() - //require.NoError(t, err) - - // commonCfg := config.DefaultCommonConfig() // set up a finality provider _, fpSK := tm.CreateFinalityProvider(t) @@ -134,11 +119,6 @@ func TestActivatingDelegation(t *testing.T) { tm.SubmitInclusionProof(t, stakingMsgTxHash.String(), stakingTxInfo) - // // make sure we didn't submit any "invalid" incl proof - // require.Eventually(t, func() bool { - // return promtestutil.ToFloat64(stakingTrackerMetrics.FailedReportedActivateDelegations) == 0 - // }, eventuallyWaitTimeOut, eventuallyPollTime) - // created delegation lacks inclusion proof, once created it will be in // pending status, once convenant signatures are added it will be in verified status, // and once the stakingEventWatcher submits MsgAddBTCDelegationInclusionProof it will From 63dd026ed097e4c86f5adfa849b15581327c844e Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 12:39:18 +0530 Subject: [PATCH 19/32] add assertions --- e2etest/e2e_test.go | 67 ++++++++++++++++++++++++-- e2etest/test_manager_btcstaking.go | 26 +++++----- internal/services/finality_provider.go | 8 ++- 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index d8670d4..d42b2de 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -1,6 +1,7 @@ package e2etest import ( + "context" "encoding/hex" "encoding/json" "fmt" @@ -9,6 +10,7 @@ import ( "testing" "time" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" bbndatagen "github.com/babylonlabs-io/babylon/testutil/datagen" queuecli "github.com/babylonlabs-io/staking-queue-client/client" "github.com/babylonlabs-io/staking-queue-client/config" @@ -71,13 +73,60 @@ func TestActivatingDelegation(t *testing.T) { tm.CatchUpBTCLightClient(t) // set up a finality provider - _, fpSK := tm.CreateFinalityProvider(t) + fpPK, fpSK := tm.CreateFinalityProvider(t) + + // check if the finality provider is stored in indexer db + require.Eventually(t, func() bool { + fp, err := tm.DbClient.GetFinalityProviderByBtcPk(context.Background(), fpPK.BtcPk.MarshalHex()) + if err != nil { + return false + } + return fp != nil && fp.BtcPk == fpPK.BtcPk.MarshalHex() + }, eventuallyWaitTimeOut, eventuallyPollTime) + // set up a BTC delegation - stakingMsgTx, stakingSlashingInfo, _, _ := tm.CreateBTCDelegationWithoutIncl(t, fpSK) + stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, _ := tm.CreateBTCDelegationWithoutIncl(t, fpSK) stakingMsgTxHash := stakingMsgTx.TxHash() + // check if BTC delegation state in indexer db is PENDING + require.Eventually(t, func() bool { + state, err := tm.DbClient.GetBTCDelegationState(context.Background(), stakingMsgTxHash.String()) + if err != nil { + return false + } + return state.String() == types.StatePending.String() + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // generate and insert new covenant signature + slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + unbondingSlashingPathSpendInfo, err := unbondingSlashingInfo.UnbondingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) + require.NoError(t, err) + tm.addCovenantSig( + t, + tm.BabylonClient.MustGetAddr(), + stakingMsgTx, + &stakingMsgTxHash, + fpSK, slashingSpendPath, + stakingSlashingInfo, + unbondingSlashingInfo, + unbondingSlashingPathSpendInfo, + stakingOutIdx, + ) + + // check if BTC delegation state in indexer db is VERIFIED + require.Eventually(t, func() bool { + state, err := tm.DbClient.GetBTCDelegationState(context.Background(), stakingMsgTxHash.String()) + if err != nil { + return false + } + return state.String() == types.StateVerified.String() + }, eventuallyWaitTimeOut, eventuallyPollTime) + // send staking tx to Bitcoin node's mempool - _, err := tm.WalletClient.SendRawTransaction(stakingMsgTx, true) + _, err = tm.WalletClient.SendRawTransaction(stakingMsgTx, true) require.NoError(t, err) require.Eventually(t, func() bool { @@ -114,7 +163,6 @@ func TestActivatingDelegation(t *testing.T) { } tm.CatchUpBTCLightClient(t) }() - wg.Wait() tm.SubmitInclusionProof(t, stakingMsgTxHash.String(), stakingTxInfo) @@ -130,8 +178,17 @@ func TestActivatingDelegation(t *testing.T) { return resp.BtcDelegation.Active }, eventuallyWaitTimeOut, eventuallyPollTime) + // check if BTC delegation state in indexer db is ACTIVE + require.Eventually(t, func() bool { + state, err := tm.DbClient.GetBTCDelegationState(context.Background(), stakingMsgTxHash.String()) + if err != nil { + return false + } + return state.String() == types.StateActive.String() + }, eventuallyWaitTimeOut, eventuallyPollTime) + // check that the staking tx is already stored - _ = tm.WaitForStakingTxStored(t, stakingMsgTxHash.String()) + // _ = tm.WaitForStakingTxStored(t, stakingMsgTxHash.String()) // check that the staking event is already stored tm.CheckNextStakingEvent(t, stakingMsgTxHash.String()) diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index 783921b..ee53c7f 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -260,7 +260,7 @@ func (tm *TestManager) CreateBTCDelegationWithoutIncl( require.NoError(t, err) // Generate all data necessary for unbonding - unbondingSlashingInfo, unbondingSlashingPathSpendInfo, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( + unbondingSlashingInfo, _, unbondingTxBytes, slashingTxSig := tm.createUnbondingData( t, fpPK, bsParams, @@ -298,18 +298,18 @@ func (tm *TestManager) CreateBTCDelegationWithoutIncl( require.NoError(t, err) t.Logf("submitted MsgCreateBTCDelegation") - // generate and insert new covenant signature, to activate the BTC delegation - tm.addCovenantSig( - t, - signerAddr, - stakingMsgTx, - stakingMsgTxHash, - fpSK, slashingSpendPath, - stakingSlashingInfo, - unbondingSlashingInfo, - unbondingSlashingPathSpendInfo, - stakingOutIdx, - ) + //// generate and insert new covenant signature, to activate the BTC delegation + //tm.addCovenantSig( + // t, + // signerAddr, + // stakingMsgTx, + // stakingMsgTxHash, + // fpSK, slashingSpendPath, + // stakingSlashingInfo, + // unbondingSlashingInfo, + // unbondingSlashingPathSpendInfo, + // stakingOutIdx, + //) return stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey } diff --git a/internal/services/finality_provider.go b/internal/services/finality_provider.go index 424a38e..426bcf1 100644 --- a/internal/services/finality_provider.go +++ b/internal/services/finality_provider.go @@ -112,7 +112,13 @@ func (s *Service) processFinalityProviderStateChangeEvent( func (s *Service) validateFinalityProviderCreatedEvent( fpCreated *bbntypes.EventFinalityProviderCreated, ) *types.Error { - // TODO: Implement validation logic + if fpCreated.BtcPkHex == "" { + return types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + "finality provider created event missing btc public key", + ) + } return nil } From 36c2727ed038fede9f99a3e2a4a8576ce5aab2fa Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 12:55:32 +0530 Subject: [PATCH 20/32] fix --- e2etest/e2e_test.go | 54 +++++++++-------------------------------- e2etest/test_manager.go | 38 +++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index d42b2de..00cf5da 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -65,6 +65,7 @@ func TestQueueConsumer(t *testing.T) { func TestActivatingDelegation(t *testing.T) { // segwit is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) + ctx := context.Background() tm := StartManager(t, numMatureOutputs, defaultEpochInterval) defer tm.Stop(t) @@ -76,26 +77,14 @@ func TestActivatingDelegation(t *testing.T) { fpPK, fpSK := tm.CreateFinalityProvider(t) // check if the finality provider is stored in indexer db - require.Eventually(t, func() bool { - fp, err := tm.DbClient.GetFinalityProviderByBtcPk(context.Background(), fpPK.BtcPk.MarshalHex()) - if err != nil { - return false - } - return fp != nil && fp.BtcPk == fpPK.BtcPk.MarshalHex() - }, eventuallyWaitTimeOut, eventuallyPollTime) + tm.WaitForFinalityProviderStored(t, ctx, fpPK.BtcPk.MarshalHex()) // set up a BTC delegation stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, _ := tm.CreateBTCDelegationWithoutIncl(t, fpSK) stakingMsgTxHash := stakingMsgTx.TxHash() - // check if BTC delegation state in indexer db is PENDING - require.Eventually(t, func() bool { - state, err := tm.DbClient.GetBTCDelegationState(context.Background(), stakingMsgTxHash.String()) - if err != nil { - return false - } - return state.String() == types.StatePending.String() - }, eventuallyWaitTimeOut, eventuallyPollTime) + // check if delegation is PENDING in indexer db + tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StatePending) // generate and insert new covenant signature slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() @@ -116,14 +105,8 @@ func TestActivatingDelegation(t *testing.T) { stakingOutIdx, ) - // check if BTC delegation state in indexer db is VERIFIED - require.Eventually(t, func() bool { - state, err := tm.DbClient.GetBTCDelegationState(context.Background(), stakingMsgTxHash.String()) - if err != nil { - return false - } - return state.String() == types.StateVerified.String() - }, eventuallyWaitTimeOut, eventuallyPollTime) + // check if delegation is VERIFIED in indexer db + tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateVerified) // send staking tx to Bitcoin node's mempool _, err = tm.WalletClient.SendRawTransaction(stakingMsgTx, true) @@ -132,13 +115,10 @@ func TestActivatingDelegation(t *testing.T) { require.Eventually(t, func() bool { return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&stakingMsgTxHash})) == 1 }, eventuallyWaitTimeOut, eventuallyPollTime) - mBlock := tm.mineBlock(t) require.Equal(t, 2, len(mBlock.Transactions)) - // get spv proof of the BTC staking tx stakingTxInfo := getTxInfo(t, mBlock) - // wait until staking tx is on Bitcoin require.Eventually(t, func() bool { _, err := tm.WalletClient.GetRawTransaction(&stakingMsgTxHash) @@ -167,10 +147,7 @@ func TestActivatingDelegation(t *testing.T) { tm.SubmitInclusionProof(t, stakingMsgTxHash.String(), stakingTxInfo) - // created delegation lacks inclusion proof, once created it will be in - // pending status, once convenant signatures are added it will be in verified status, - // and once the stakingEventWatcher submits MsgAddBTCDelegationInclusionProof it will - // be in active status + // wait for delegation to be ACTIVE in Babylon node require.Eventually(t, func() bool { resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) require.NoError(t, err) @@ -178,18 +155,9 @@ func TestActivatingDelegation(t *testing.T) { return resp.BtcDelegation.Active }, eventuallyWaitTimeOut, eventuallyPollTime) - // check if BTC delegation state in indexer db is ACTIVE - require.Eventually(t, func() bool { - state, err := tm.DbClient.GetBTCDelegationState(context.Background(), stakingMsgTxHash.String()) - if err != nil { - return false - } - return state.String() == types.StateActive.String() - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // check that the staking tx is already stored - // _ = tm.WaitForStakingTxStored(t, stakingMsgTxHash.String()) + // check if delegation is ACTIVE in indexer db + tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateActive) - // check that the staking event is already stored - tm.CheckNextStakingEvent(t, stakingMsgTxHash.String()) + // consume active staking event + tm.CheckNextActiveStakingEvent(t, stakingMsgTxHash.String()) } diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index d61e030..29682c9 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -20,6 +20,7 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" "github.com/babylonlabs-io/babylon-staking-indexer/internal/observability/metrics" "github.com/babylonlabs-io/babylon-staking-indexer/internal/services" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" _ "github.com/babylonlabs-io/babylon/app/params" bbnclient "github.com/babylonlabs-io/babylon/client/client" bbncfg "github.com/babylonlabs-io/babylon/client/config" @@ -346,24 +347,45 @@ func importPrivateKey(btcHandler *BitcoindTestHandler) (*btcec.PrivateKey, error return privKey, nil } -func (tm *TestManager) WaitForStakingTxStored(t *testing.T, stakingTxHashHex string) *model.BTCDelegationDetails { - var storedDelegation model.BTCDelegationDetails +func (tm *TestManager) WaitForDelegationStored(t *testing.T, ctx context.Context, stakingTxHashHex string, expectedState types.DelegationState) { + var storedDelegation *model.BTCDelegationDetails + + // Wait for delegation to be stored in DB and match expected state require.Eventually(t, func() bool { - x, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), stakingTxHashHex) - if err != nil || x == nil { + delegation, err := tm.DbClient.GetBTCDelegationByStakingTxHash(ctx, stakingTxHashHex) + if err != nil || delegation == nil { + t.Logf("Waiting for delegation %s to be stored, current error: %v", stakingTxHashHex, err) return false } - storedDelegation = *x + if delegation.State != expectedState { + t.Logf("Waiting for delegation %s state to be %s, current state: %s", + stakingTxHashHex, expectedState, delegation.State) + return false + } + + storedDelegation = delegation return true }, eventuallyWaitTimeOut, eventuallyPollTime) - require.Equal(t, stakingTxHashHex, storedDelegation.StakingTxHashHex) + require.NotNil(t, storedDelegation) + require.Equal(t, stakingTxHashHex, storedDelegation.StakingTxHashHex, + "Stored delegation hash does not match expected") + require.Equal(t, expectedState.String(), storedDelegation.State.String(), + "Stored delegation state does not match expected") +} - return &storedDelegation +func (tm *TestManager) WaitForFinalityProviderStored(t *testing.T, ctx context.Context, fpPKHex string) { + require.Eventually(t, func() bool { + fp, err := tm.DbClient.GetFinalityProviderByBtcPk(ctx, fpPKHex) + if err != nil || fp == nil { + return false + } + return fp != nil && fp.BtcPk == fpPKHex + }, eventuallyWaitTimeOut, eventuallyPollTime) } -func (tm *TestManager) CheckNextStakingEvent(t *testing.T, stakingTxHashHex string) { +func (tm *TestManager) CheckNextActiveStakingEvent(t *testing.T, stakingTxHashHex string) { stakingEventBytes := <-tm.ActiveStakingEventChan var activeStakingEvent queuecli.StakingEvent err := json.Unmarshal([]byte(stakingEventBytes.Body), &activeStakingEvent) From f4bb0e82443073796bfeccfa19eda62fdb3f875b Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 12:59:10 +0530 Subject: [PATCH 21/32] improve comments --- e2etest/e2e_test.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 00cf5da..67e17fe 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -63,7 +63,7 @@ func TestQueueConsumer(t *testing.T) { // eventually become "active". // Specifically, that stakingEventWatcher will send a MsgAddBTCDelegationInclusionProof to do so. func TestActivatingDelegation(t *testing.T) { - // segwit is activated at height 300. It's necessary for staking/slashing tx + // Segw is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) ctx := context.Background() @@ -73,20 +73,20 @@ func TestActivatingDelegation(t *testing.T) { // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) - // set up a finality provider + // Create finality provider in Babylon node fpPK, fpSK := tm.CreateFinalityProvider(t) - // check if the finality provider is stored in indexer db + // Wait for finality provider to be stored in Indexer DB tm.WaitForFinalityProviderStored(t, ctx, fpPK.BtcPk.MarshalHex()) - // set up a BTC delegation + // Create BTC delegation without inclusion proof in Babylon node stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, _ := tm.CreateBTCDelegationWithoutIncl(t, fpSK) stakingMsgTxHash := stakingMsgTx.TxHash() - // check if delegation is PENDING in indexer db + // Wait for delegation to be PENDING in Indexer DB tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StatePending) - // generate and insert new covenant signature + // Generate and insert new covenant signature in Babylon node slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() require.NoError(t, err) unbondingSlashingPathSpendInfo, err := unbondingSlashingInfo.UnbondingInfo.SlashingPathSpendInfo() @@ -105,21 +105,25 @@ func TestActivatingDelegation(t *testing.T) { stakingOutIdx, ) - // check if delegation is VERIFIED in indexer db + // Wait for delegation to be VERIFIED in Indexer DB tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateVerified) - // send staking tx to Bitcoin node's mempool + // Send staking tx to Bitcoin node's mempool _, err = tm.WalletClient.SendRawTransaction(stakingMsgTx, true) require.NoError(t, err) require.Eventually(t, func() bool { return len(tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&stakingMsgTxHash})) == 1 }, eventuallyWaitTimeOut, eventuallyPollTime) + + // Mine a block to make sure the staking tx is on Bitcoin mBlock := tm.mineBlock(t) require.Equal(t, 2, len(mBlock.Transactions)) - // get spv proof of the BTC staking tx + + // Get spv proof of the BTC staking tx stakingTxInfo := getTxInfo(t, mBlock) - // wait until staking tx is on Bitcoin + + // Wait until staking tx is on Bitcoin require.Eventually(t, func() bool { _, err := tm.WalletClient.GetRawTransaction(&stakingMsgTxHash) return err == nil @@ -145,9 +149,10 @@ func TestActivatingDelegation(t *testing.T) { }() wg.Wait() + // Submit inclusion proof to Babylon node tm.SubmitInclusionProof(t, stakingMsgTxHash.String(), stakingTxInfo) - // wait for delegation to be ACTIVE in Babylon node + // Wait for delegation to be ACTIVE in Babylon node require.Eventually(t, func() bool { resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) require.NoError(t, err) @@ -155,9 +160,9 @@ func TestActivatingDelegation(t *testing.T) { return resp.BtcDelegation.Active }, eventuallyWaitTimeOut, eventuallyPollTime) - // check if delegation is ACTIVE in indexer db + // Wait for delegation to be ACTIVE in Indexer DB tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateActive) - // consume active staking event + // Consume active staking event emitted by Indexer tm.CheckNextActiveStakingEvent(t, stakingMsgTxHash.String()) } From 86b642555b90a5f230fc818f62eb113afd5b6900 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 13:02:01 +0530 Subject: [PATCH 22/32] fix --- e2etest/e2e_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 67e17fe..e60e742 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -59,10 +59,15 @@ func TestQueueConsumer(t *testing.T) { } } -// TestActivatingDelegation verifies that a delegation created without an inclusion proof will -// eventually become "active". -// Specifically, that stakingEventWatcher will send a MsgAddBTCDelegationInclusionProof to do so. -func TestActivatingDelegation(t *testing.T) { +// TestStakingLifecycle verifies the BTC delegation state transitions +// 1. Create BTC delegation without inclusion proof in Babylon node +// 2. Wait for delegation to be PENDING in Indexer DB +// 3. Generate and insert new covenant signature in Babylon node +// 4. Submit inclusion proof to Babylon node +// 5. Wait for delegation to be ACTIVE in Babylon node +// 6. Wait for delegation to be ACTIVE in Indexer DB +// 7. Verify active staking event emitted by Indexer +func TestStakingLifecycle(t *testing.T) { // Segw is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) ctx := context.Background() From a4d2cb4dc2a8d1ad8302807f905d50d4e63a4f30 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 13:33:55 +0530 Subject: [PATCH 23/32] add unbonding tests --- e2etest/e2e_test.go | 31 ++++++++++-- e2etest/test_manager.go | 81 +++++++++++++++++++----------- e2etest/test_manager_btcstaking.go | 13 ----- 3 files changed, 80 insertions(+), 45 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index e60e742..06ee00a 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -12,6 +12,7 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" bbndatagen "github.com/babylonlabs-io/babylon/testutil/datagen" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" queuecli "github.com/babylonlabs-io/staking-queue-client/client" "github.com/babylonlabs-io/staking-queue-client/config" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -89,7 +90,7 @@ func TestStakingLifecycle(t *testing.T) { stakingMsgTxHash := stakingMsgTx.TxHash() // Wait for delegation to be PENDING in Indexer DB - tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StatePending) + tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StatePending, nil) // Generate and insert new covenant signature in Babylon node slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() @@ -111,7 +112,7 @@ func TestStakingLifecycle(t *testing.T) { ) // Wait for delegation to be VERIFIED in Indexer DB - tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateVerified) + tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateVerified, nil) // Send staking tx to Bitcoin node's mempool _, err = tm.WalletClient.SendRawTransaction(stakingMsgTx, true) @@ -160,14 +161,36 @@ func TestStakingLifecycle(t *testing.T) { // Wait for delegation to be ACTIVE in Babylon node require.Eventually(t, func() bool { resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) - require.NoError(t, err) + if err != nil { + return false + } return resp.BtcDelegation.Active }, eventuallyWaitTimeOut, eventuallyPollTime) // Wait for delegation to be ACTIVE in Indexer DB - tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateActive) + tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateActive, nil) // Consume active staking event emitted by Indexer tm.CheckNextActiveStakingEvent(t, stakingMsgTxHash.String()) + + // Early unbonding on Babylon node + _, _ = tm.Undelegate(t, stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey, func() { tm.CatchUpBTCLightClient(t) }) + + // Wait for delegation to be UNBONDED in Babylon node + require.Eventually(t, func() bool { + resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) + if err != nil { + return false + } + + return resp.BtcDelegation.StatusDesc == bstypes.BTCDelegationStatus_UNBONDED.String() + }, eventuallyWaitTimeOut, eventuallyPollTime) + + // Wait for delegation to be UNBONDING and sub-state to be EARLY_UNBONDING in Indexer DB + expectedSubState := types.SubStateEarlyUnbonding + tm.WaitForDelegationStored(t, ctx, stakingMsgTxHash.String(), types.StateUnbonding, &expectedSubState) + + // Consume unbonding staking event emitted by Indexer + tm.CheckNextUnbondingStakingEvent(t, stakingMsgTxHash.String()) } diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 29682c9..c4fd42a 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -52,16 +52,17 @@ var ( ) type TestManager struct { - BitcoindHandler *BitcoindTestHandler - BabylonClient *bbnclient.Client - BTCClient *btcclient.BTCClient - WalletClient *rpcclient.Client - WalletPrivKey *btcec.PrivateKey - Config *config.Config - manager *container.Manager - DbClient *db.Database - QueueConsumer *queuemngr.QueueManager - ActiveStakingEventChan <-chan queuecli.QueueMessage + BitcoindHandler *BitcoindTestHandler + BabylonClient *bbnclient.Client + BTCClient *btcclient.BTCClient + WalletClient *rpcclient.Client + WalletPrivKey *btcec.PrivateKey + Config *config.Config + manager *container.Manager + DbClient *db.Database + QueueConsumer *queuemngr.QueueManager + ActiveStakingEventChan <-chan queuecli.QueueMessage + UnbondingStakingEventChan <-chan queuecli.QueueMessage } // StartManager creates a test manager @@ -172,6 +173,9 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u activeStakingEventChan, err := queueConsumer.ActiveStakingQueue.ReceiveMessages() require.NoError(t, err) + unbondingStakingEventChan, err := queueConsumer.UnbondingStakingQueue.ReceiveMessages() + require.NoError(t, err) + var wg sync.WaitGroup wg.Add(1) go func() { @@ -182,16 +186,17 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u time.Sleep(3 * time.Second) return &TestManager{ - WalletClient: rpcclient, - BabylonClient: babylonClient, - BitcoindHandler: btcHandler, - BTCClient: btcClient, - Config: cfg, - WalletPrivKey: walletPrivKey, - manager: manager, - ActiveStakingEventChan: activeStakingEventChan, - DbClient: dbClient, - QueueConsumer: queueConsumer, + WalletClient: rpcclient, + BabylonClient: babylonClient, + BitcoindHandler: btcHandler, + BTCClient: btcClient, + Config: cfg, + WalletPrivKey: walletPrivKey, + manager: manager, + ActiveStakingEventChan: activeStakingEventChan, + UnbondingStakingEventChan: unbondingStakingEventChan, + DbClient: dbClient, + QueueConsumer: queueConsumer, } } @@ -347,7 +352,12 @@ func importPrivateKey(btcHandler *BitcoindTestHandler) (*btcec.PrivateKey, error return privKey, nil } -func (tm *TestManager) WaitForDelegationStored(t *testing.T, ctx context.Context, stakingTxHashHex string, expectedState types.DelegationState) { +func (tm *TestManager) WaitForDelegationStored(t *testing.T, + ctx context.Context, + stakingTxHashHex string, + expectedState types.DelegationState, + expectedSubState *types.DelegationSubState, +) { var storedDelegation *model.BTCDelegationDetails // Wait for delegation to be stored in DB and match expected state @@ -364,6 +374,12 @@ func (tm *TestManager) WaitForDelegationStored(t *testing.T, ctx context.Context return false } + if expectedSubState != nil && delegation.SubState != *expectedSubState { + t.Logf("Waiting for delegation %s sub-state to be %s, current sub-state: %s", + stakingTxHashHex, expectedSubState, delegation.SubState) + return false + } + storedDelegation = delegation return true }, eventuallyWaitTimeOut, eventuallyPollTime) @@ -373,6 +389,10 @@ func (tm *TestManager) WaitForDelegationStored(t *testing.T, ctx context.Context "Stored delegation hash does not match expected") require.Equal(t, expectedState.String(), storedDelegation.State.String(), "Stored delegation state does not match expected") + if expectedSubState != nil { + require.Equal(t, expectedSubState.String(), storedDelegation.SubState.String(), + "Stored delegation sub-state does not match expected") + } } func (tm *TestManager) WaitForFinalityProviderStored(t *testing.T, ctx context.Context, fpPKHex string) { @@ -391,15 +411,20 @@ func (tm *TestManager) CheckNextActiveStakingEvent(t *testing.T, stakingTxHashHe err := json.Unmarshal([]byte(stakingEventBytes.Body), &activeStakingEvent) require.NoError(t, err) - storedStakingTx, err := tm.DbClient.GetBTCDelegationByStakingTxHash(context.Background(), stakingTxHashHex) - require.NotNil(t, storedStakingTx) - require.NoError(t, err) require.Equal(t, stakingTxHashHex, activeStakingEvent.StakingTxHashHex) - require.Equal(t, storedStakingTx.StakingTxHashHex, activeStakingEvent.StakingTxHashHex) - require.Equal(t, storedStakingTx.StakingAmount, activeStakingEvent.StakingAmount) - require.Equal(t, storedStakingTx.StakerBtcPkHex, activeStakingEvent.StakerBtcPkHex) - require.Equal(t, storedStakingTx.FinalityProviderBtcPksHex, activeStakingEvent.FinalityProviderBtcPksHex) err = tm.QueueConsumer.ActiveStakingQueue.DeleteMessage(stakingEventBytes.Receipt) require.NoError(t, err) } + +func (tm *TestManager) CheckNextUnbondingStakingEvent(t *testing.T, stakingTxHashHex string) { + unbondingEventBytes := <-tm.UnbondingStakingEventChan + var unbondingStakingEvent queuecli.StakingEvent + err := json.Unmarshal([]byte(unbondingEventBytes.Body), &unbondingStakingEvent) + require.NoError(t, err) + + require.Equal(t, stakingTxHashHex, unbondingStakingEvent.StakingTxHashHex) + + err = tm.QueueConsumer.UnbondingStakingQueue.DeleteMessage(unbondingEventBytes.Receipt) + require.NoError(t, err) +} diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index ee53c7f..98d8224 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -298,19 +298,6 @@ func (tm *TestManager) CreateBTCDelegationWithoutIncl( require.NoError(t, err) t.Logf("submitted MsgCreateBTCDelegation") - //// generate and insert new covenant signature, to activate the BTC delegation - //tm.addCovenantSig( - // t, - // signerAddr, - // stakingMsgTx, - // stakingMsgTxHash, - // fpSK, slashingSpendPath, - // stakingSlashingInfo, - // unbondingSlashingInfo, - // unbondingSlashingPathSpendInfo, - // stakingOutIdx, - //) - return stakingMsgTx, stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey } From 88b1ead7b840988984ba27ea25d6e6ab203ae66c Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 13:40:32 +0530 Subject: [PATCH 24/32] fix --- cmd/babylon-staking-indexer/main.go | 11 +++++++++-- e2etest/e2e_test.go | 11 ++++++++++- e2etest/test_manager.go | 24 +++++------------------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/cmd/babylon-staking-indexer/main.go b/cmd/babylon-staking-indexer/main.go index 4e88645..701578f 100644 --- a/cmd/babylon-staking-indexer/main.go +++ b/cmd/babylon-staking-indexer/main.go @@ -46,8 +46,15 @@ func main() { } // Create a basic zap logger - zapLogger, _ := zap.NewProduction() - defer zapLogger.Sync() + zapLogger, err := zap.NewProduction() + if err != nil { + log.Fatal().Err(err).Msg("error while creating zap logger") + } + defer func() { + if err := zapLogger.Sync(); err != nil { + log.Fatal().Err(err).Msg("error while syncing zap logger") + } + }() queueConsumer, err := queuemngr.NewQueueManager(&cfg.Queue, zapLogger) if err != nil { diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 06ee00a..45a08bd 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -31,7 +31,10 @@ func TestQueueConsumer(t *testing.T) { stakingChan, err := queueConsumer.ActiveStakingQueue.ReceiveMessages() require.NoError(t, err) - defer queueConsumer.Stop() + defer func() { + err := queueConsumer.Stop() + require.NoError(t, err) + }() n := 1 r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -68,7 +71,13 @@ func TestQueueConsumer(t *testing.T) { // 5. Wait for delegation to be ACTIVE in Babylon node // 6. Wait for delegation to be ACTIVE in Indexer DB // 7. Verify active staking event emitted by Indexer +// 8. Early unbonding on Babylon node +// 9. Wait for delegation to be UNBONDED in Babylon node +// 10. Wait for delegation to be UNBONDING and sub-state to be EARLY_UNBONDING in Indexer DB +// 11. Verify unbonding staking event emitted by Indexer func TestStakingLifecycle(t *testing.T) { + // PENDING -> VERIFIED -> ACTIVE -> UNBONDING/EARLY_UNBONDING + // Segw is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) ctx := context.Background() diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index c4fd42a..bd232be 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -41,14 +41,9 @@ import ( ) var ( - // submitterAddrStr = "bbn1eppc73j56382wjn6nnq3quu5eye4pmm087xfdh" //nolint:unused - // babylonTag = []byte{1, 2, 3, 4} //nolint:unused - // babylonTagHex = hex.EncodeToString(babylonTag) //nolint:unused - eventuallyWaitTimeOut = 40 * time.Second eventuallyPollTime = 1 * time.Second regtestParams = &chaincfg.RegressionNetParams - // defaultEpochInterval = uint(400) //nolint:unused ) type TestManager struct { @@ -66,7 +61,6 @@ type TestManager struct { } // StartManager creates a test manager -// NOTE: uses btc client with zmq func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval uint) *TestManager { manager, err := container.NewManager(t) require.NoError(t, err) @@ -75,9 +69,6 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u bitcoind := btcHandler.Start(t) passphrase := "pass" _ = btcHandler.CreateWallet("default", passphrase) - // resp := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) - // minerAddressDecoded, err := btcutil.DecodeAddress(resp.Address, regtestParams) - // require.NoError(t, err) cfg := DefaultStakingIndexerConfig() @@ -89,8 +80,6 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u require.NoError(t, err) err = rpcclient.WalletPassphrase(passphrase, 800) require.NoError(t, err) - // walletPrivKey, err := rpcclient.DumpPrivKey(minerAddressDecoded) - // require.NoError(t, err) walletPrivKey, err := importPrivateKey(btcHandler) require.NoError(t, err) @@ -151,9 +140,6 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u queueConsumer, err := queuemngr.NewQueueManager(&cfg.Queue, zap.NewNop()) require.NoError(t, err) - // queueConsumer, err := setupTestQueueConsumer(t, &cfg.Queue) - // require.NoError(t, err) - btcNotifier, err := btcclient.NewBTCNotifier( &cfg.BTC, &btcclient.EmptyHintCache{}, @@ -186,17 +172,17 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u time.Sleep(3 * time.Second) return &TestManager{ - WalletClient: rpcclient, - BabylonClient: babylonClient, BitcoindHandler: btcHandler, + BabylonClient: babylonClient, BTCClient: btcClient, - Config: cfg, + WalletClient: rpcclient, WalletPrivKey: walletPrivKey, + Config: cfg, manager: manager, - ActiveStakingEventChan: activeStakingEventChan, - UnbondingStakingEventChan: unbondingStakingEventChan, DbClient: dbClient, QueueConsumer: queueConsumer, + ActiveStakingEventChan: activeStakingEventChan, + UnbondingStakingEventChan: unbondingStakingEventChan, } } From 54ff114a43328281accea38714e93c70d12efbe1 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 13:58:11 +0530 Subject: [PATCH 25/32] fix config --- e2etest/e2e_test.go | 5 ++--- e2etest/test_manager.go | 17 ----------------- internal/config/btc.go | 29 ----------------------------- internal/config/config.go | 26 ++++++++++++++++++++++---- internal/config/db.go | 17 ----------------- internal/config/metrics.go | 7 ------- 6 files changed, 24 insertions(+), 77 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 45a08bd..18e68fa 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -63,7 +63,8 @@ func TestQueueConsumer(t *testing.T) { } } -// TestStakingLifecycle verifies the BTC delegation state transitions +// TestStakingLifecycle verifies the happy path of BTC delegation state transitions +// PENDING -> VERIFIED -> ACTIVE -> UNBONDING/EARLY_UNBONDING // 1. Create BTC delegation without inclusion proof in Babylon node // 2. Wait for delegation to be PENDING in Indexer DB // 3. Generate and insert new covenant signature in Babylon node @@ -76,8 +77,6 @@ func TestQueueConsumer(t *testing.T) { // 10. Wait for delegation to be UNBONDING and sub-state to be EARLY_UNBONDING in Indexer DB // 11. Verify unbonding staking event emitted by Indexer func TestStakingLifecycle(t *testing.T) { - // PENDING -> VERIFIED -> ACTIVE -> UNBONDING/EARLY_UNBONDING - // Segw is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) ctx := context.Background() diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index bd232be..c204ba2 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -229,23 +229,6 @@ func tempDir(t *testing.T) (string, error) { func DefaultStakingIndexerConfig() *config.Config { defaultConfig := config.DefaultConfig() - - // enable emitting extra events for testing - //defaultConfig.ExtraEventEnabled = true - - // both wallet and node are bitcoind - defaultConfig.BTC.NetParams = regtestParams.Name - - bitcoindHost := "127.0.0.1:18443" - bitcoindUser := "user" - bitcoindPass := "pass" - - defaultConfig.BTC.RPCHost = bitcoindHost - defaultConfig.BTC.RPCUser = bitcoindUser - defaultConfig.BTC.RPCPass = bitcoindPass - defaultConfig.BTC.BlockPollingInterval = 1 * time.Second - defaultConfig.BTC.TxPollingInterval = 1 * time.Second - defaultConfig.Queue.QueueProcessingTimeout = time.Duration(500) * time.Second defaultConfig.Queue.ReQueueDelayTime = time.Duration(300) * time.Second diff --git a/internal/config/btc.go b/internal/config/btc.go index ee2cd81..14fa5d4 100644 --- a/internal/config/btc.go +++ b/internal/config/btc.go @@ -82,32 +82,3 @@ func (cfg *BTCConfig) Validate() error { return nil } - -const ( - // default rpc port of signet is 38332 - defaultBitcoindRpcHost = "127.0.0.1:38332" - defaultBitcoindRPCUser = "user" - defaultBitcoindRPCPass = "pass" - defaultBitcoindBlockCacheSize = 20 * 1024 * 1024 // 20 MB - defaultBlockPollingInterval = 30 * time.Second - defaultTxPollingInterval = 30 * time.Second - defaultMaxRetryTimes = 5 - defaultRetryInterval = 500 * time.Millisecond - // DefaultTxPollingJitter defines the default TxPollingIntervalJitter - // to be used for bitcoind backend. - DefaultTxPollingJitter = 0.5 -) - -func DefaultBTCConfig() *BTCConfig { - return &BTCConfig{ - RPCHost: defaultBitcoindRpcHost, - RPCUser: defaultBitcoindRPCUser, - RPCPass: defaultBitcoindRPCPass, - BlockPollingInterval: defaultBlockPollingInterval, - TxPollingInterval: defaultTxPollingInterval, - BlockCacheSize: defaultBitcoindBlockCacheSize, - MaxRetryTimes: defaultMaxRetryTimes, - RetryInterval: defaultRetryInterval, - NetParams: "regtest", - } -} diff --git a/internal/config/config.go b/internal/config/config.go index bf0529c..7c31ceb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -87,8 +87,23 @@ func New(cfgFile string) (*Config, error) { func DefaultConfig() *Config { bbnCfg := bbncfg.DefaultBabylonConfig() cfg := &Config{ - BTC: *DefaultBTCConfig(), - Db: *DefaultDBConfig(), + BTC: BTCConfig{ + RPCHost: "127.0.0.1:18443", + RPCUser: "user", + RPCPass: "pass", + BlockPollingInterval: 30 * time.Second, + TxPollingInterval: 30 * time.Second, + BlockCacheSize: 20 * 1024 * 1024, // 20 MB + MaxRetryTimes: 5, + RetryInterval: 500 * time.Millisecond, + NetParams: "regtest", + }, + Db: DbConfig{ + Address: "mongodb://localhost:27019/?replicaSet=RS&directConnection=true", + Username: "root", + Password: "example", + DbName: "babylon-staking-indexer", + }, BBN: BBNConfig{ RPCAddr: bbnCfg.RPCAddr, Timeout: bbnCfg.Timeout, @@ -100,8 +115,11 @@ func DefaultConfig() *Config { ExpiryCheckerPollingInterval: 1 * time.Second, ExpiredDelegationsLimit: 1000, }, - Queue: *queue.DefaultQueueConfig(), - Metrics: DefaultMetricsConfig(), + Queue: *queue.DefaultQueueConfig(), + Metrics: MetricsConfig{ + Host: "0.0.0.0", + Port: 2112, + }, } if err := cfg.Validate(); err != nil { diff --git a/internal/config/db.go b/internal/config/db.go index 984b756..03fbc94 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -59,20 +59,3 @@ func (cfg *DbConfig) Validate() error { return nil } - -const ( - defaultDbAddress = "mongodb://localhost:27019/?replicaSet=RS&directConnection=true" - defaultDbUsername = "root" - defaultDbPassword = "example" - defaultDbName = "babylon-staking-indexer" -) - -func DefaultDBConfig() *DbConfig { - return &DbConfig{ - Address: defaultDbAddress, - Username: defaultDbUsername, - Password: defaultDbPassword, - DbName: defaultDbName, - } - -} diff --git a/internal/config/metrics.go b/internal/config/metrics.go index 159d508..38e950f 100644 --- a/internal/config/metrics.go +++ b/internal/config/metrics.go @@ -29,10 +29,3 @@ func (cfg *MetricsConfig) Validate() error { func (cfg *MetricsConfig) GetMetricsPort() int { return cfg.Port } - -func DefaultMetricsConfig() MetricsConfig { - return MetricsConfig{ - Host: "0.0.0.0", - Port: 2112, - } -} From 6f0d47ec43e9cfe072c98b6ccb6e77cfc45e9483 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 14:03:23 +0530 Subject: [PATCH 26/32] fix comments --- e2etest/e2e_test.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 18e68fa..73bbaae 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -68,14 +68,15 @@ func TestQueueConsumer(t *testing.T) { // 1. Create BTC delegation without inclusion proof in Babylon node // 2. Wait for delegation to be PENDING in Indexer DB // 3. Generate and insert new covenant signature in Babylon node -// 4. Submit inclusion proof to Babylon node -// 5. Wait for delegation to be ACTIVE in Babylon node -// 6. Wait for delegation to be ACTIVE in Indexer DB -// 7. Verify active staking event emitted by Indexer -// 8. Early unbonding on Babylon node -// 9. Wait for delegation to be UNBONDED in Babylon node -// 10. Wait for delegation to be UNBONDING and sub-state to be EARLY_UNBONDING in Indexer DB -// 11. Verify unbonding staking event emitted by Indexer +// 4. Wait for delegation to be VERIFIED in Indexer DB +// 5. Submit inclusion proof to Babylon node +// 6. Wait for delegation to be ACTIVE in Babylon node +// 7. Wait for delegation to be ACTIVE in Indexer DB +// 8. Verify active staking event emitted by Indexer +// 9. Early unbonding on Babylon node +// 10. Wait for delegation to be UNBONDED in Babylon node +// 11. Wait for delegation to be UNBONDING and sub-state to be EARLY_UNBONDING in Indexer DB +// 12. Verify unbonding staking event emitted by Indexer func TestStakingLifecycle(t *testing.T) { // Segw is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) From 8050bb272d2ce37aea037acf63dcd7de029898e6 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 14:03:58 +0530 Subject: [PATCH 27/32] fix comments --- e2etest/e2e_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 73bbaae..7bacba1 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -65,7 +65,7 @@ func TestQueueConsumer(t *testing.T) { // TestStakingLifecycle verifies the happy path of BTC delegation state transitions // PENDING -> VERIFIED -> ACTIVE -> UNBONDING/EARLY_UNBONDING -// 1. Create BTC delegation without inclusion proof in Babylon node +// 1. Create BTC delegation without inclusion proof in Babylon node (pre-approval flow) // 2. Wait for delegation to be PENDING in Indexer DB // 3. Generate and insert new covenant signature in Babylon node // 4. Wait for delegation to be VERIFIED in Indexer DB From 7c3a6882a438d2cdcd880b15b8a39329bb27ae17 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 16 Dec 2024 14:16:20 +0530 Subject: [PATCH 28/32] fix cfg --- e2etest/test_manager.go | 52 ++++++++++++++++++++++++++++++++++----- internal/config/config.go | 47 ----------------------------------- 2 files changed, 46 insertions(+), 53 deletions(-) diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index c204ba2..80e2c7b 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -27,6 +27,7 @@ import ( bbn "github.com/babylonlabs-io/babylon/types" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" queuecli "github.com/babylonlabs-io/staking-queue-client/client" + queuecfg "github.com/babylonlabs-io/staking-queue-client/config" "github.com/babylonlabs-io/staking-queue-client/queuemngr" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" @@ -36,6 +37,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" pv "github.com/cosmos/relayer/v2/relayer/provider" + "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -70,7 +72,7 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32, epochInterval u passphrase := "pass" _ = btcHandler.CreateWallet("default", passphrase) - cfg := DefaultStakingIndexerConfig() + cfg := TestConfig(t) cfg.BTC.RPCHost = fmt.Sprintf("127.0.0.1:%s", bitcoind.GetPort("18443/tcp")) @@ -227,12 +229,50 @@ func tempDir(t *testing.T) (string, error) { return tempPath, err } -func DefaultStakingIndexerConfig() *config.Config { - defaultConfig := config.DefaultConfig() - defaultConfig.Queue.QueueProcessingTimeout = time.Duration(500) * time.Second - defaultConfig.Queue.ReQueueDelayTime = time.Duration(300) * time.Second +func TestConfig(t *testing.T) *config.Config { + // TODO: ideally this should be setup through config-test.yaml + cfg := &config.Config{ + BTC: config.BTCConfig{ + RPCHost: "127.0.0.1:18443", + RPCUser: "user", + RPCPass: "pass", + BlockPollingInterval: 30 * time.Second, + TxPollingInterval: 30 * time.Second, + BlockCacheSize: 20 * 1024 * 1024, // 20 MB + MaxRetryTimes: 5, + RetryInterval: 500 * time.Millisecond, + NetParams: "regtest", + }, + Db: config.DbConfig{ + Address: "mongodb://localhost:27019/?replicaSet=RS&directConnection=true", + Username: "root", + Password: "example", + DbName: "babylon-staking-indexer", + }, + BBN: config.BBNConfig{ + RPCAddr: "http://localhost:26657", + Timeout: 20 * time.Second, + MaxRetryTimes: 3, + RetryInterval: 1 * time.Second, + }, + Poller: config.PollerConfig{ + ParamPollingInterval: 1 * time.Second, + ExpiryCheckerPollingInterval: 1 * time.Second, + ExpiredDelegationsLimit: 1000, + }, + Queue: *queuecfg.DefaultQueueConfig(), + Metrics: config.MetricsConfig{ + Host: "0.0.0.0", + Port: 2112, + }, + } + cfg.Queue.QueueProcessingTimeout = time.Duration(50) * time.Second + cfg.Queue.ReQueueDelayTime = time.Duration(100) * time.Second + + err := cfg.Validate() + require.NoError(t, err) - return defaultConfig + return cfg } // RetrieveTransactionFromMempool fetches transactions from the mempool for the given hashes diff --git a/internal/config/config.go b/internal/config/config.go index 7c31ceb..399746e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,9 +4,7 @@ import ( "fmt" "os" "strings" - "time" - bbncfg "github.com/babylonlabs-io/babylon/client/config" queue "github.com/babylonlabs-io/staking-queue-client/config" "github.com/spf13/viper" ) @@ -83,48 +81,3 @@ func New(cfgFile string) (*Config, error) { return &cfg, nil } - -func DefaultConfig() *Config { - bbnCfg := bbncfg.DefaultBabylonConfig() - cfg := &Config{ - BTC: BTCConfig{ - RPCHost: "127.0.0.1:18443", - RPCUser: "user", - RPCPass: "pass", - BlockPollingInterval: 30 * time.Second, - TxPollingInterval: 30 * time.Second, - BlockCacheSize: 20 * 1024 * 1024, // 20 MB - MaxRetryTimes: 5, - RetryInterval: 500 * time.Millisecond, - NetParams: "regtest", - }, - Db: DbConfig{ - Address: "mongodb://localhost:27019/?replicaSet=RS&directConnection=true", - Username: "root", - Password: "example", - DbName: "babylon-staking-indexer", - }, - BBN: BBNConfig{ - RPCAddr: bbnCfg.RPCAddr, - Timeout: bbnCfg.Timeout, - MaxRetryTimes: 3, - RetryInterval: 1 * time.Second, - }, - Poller: PollerConfig{ - ParamPollingInterval: 1 * time.Second, - ExpiryCheckerPollingInterval: 1 * time.Second, - ExpiredDelegationsLimit: 1000, - }, - Queue: *queue.DefaultQueueConfig(), - Metrics: MetricsConfig{ - Host: "0.0.0.0", - Port: 2112, - }, - } - - if err := cfg.Validate(); err != nil { - panic(err) - } - - return cfg -} From daf6de574a326a61c5bc27649242bddf723e6ff6 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 19 Dec 2024 18:54:18 +0530 Subject: [PATCH 29/32] pr comment: remove unused code --- Makefile | 3 +- e2etest/bitcoind_node_setup.go | 6 - e2etest/e2e_test.go | 2 +- e2etest/test_manager_btcstaking.go | 322 ----------------------------- 4 files changed, 3 insertions(+), 330 deletions(-) diff --git a/Makefile b/Makefile index d2b8f15..9a9a3e0 100644 --- a/Makefile +++ b/Makefile @@ -53,4 +53,5 @@ test: test-e2e: ./bin/local-startup.sh; - go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e + go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e; + docker compose down diff --git a/e2etest/bitcoind_node_setup.go b/e2etest/bitcoind_node_setup.go index b6b6380..b2cb88f 100644 --- a/e2etest/bitcoind_node_setup.go +++ b/e2etest/bitcoind_node_setup.go @@ -105,12 +105,6 @@ func (h *BitcoindTestHandler) CreateWallet(walletName string, passphrase string) return &response } -// InvalidateBlock invalidates blocks starting from specified block hash -func (h *BitcoindTestHandler) InvalidateBlock(blockHash string) { - _, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"invalidateblock", blockHash}) - require.NoError(h.t, err) -} - // ImportDescriptors imports a given Bitcoin address descriptor into the Bitcoind func (h *BitcoindTestHandler) ImportDescriptors(descriptor string) { _, _, err := h.m.ExecBitcoindCliCmd(h.t, []string{"importdescriptors", descriptor}) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index 7bacba1..e541f87 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -36,7 +36,7 @@ func TestQueueConsumer(t *testing.T) { require.NoError(t, err) }() - n := 1 + n := rand.Intn(10) + 1 r := rand.New(rand.NewSource(time.Now().UnixNano())) stakingEventList := make([]*queuecli.StakingEvent, 0) diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index 98d8224..cb24039 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -3,36 +3,26 @@ package e2etest import ( "bytes" "context" - "encoding/hex" "fmt" "math/rand" "testing" "time" sdkmath "cosmossdk.io/math" - "github.com/avast/retry-go/v4" "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" "github.com/babylonlabs-io/babylon/btcstaking" - txformat "github.com/babylonlabs-io/babylon/btctxformatter" - "github.com/babylonlabs-io/babylon/crypto/eots" asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" "github.com/babylonlabs-io/babylon/testutil/datagen" bbn "github.com/babylonlabs-io/babylon/types" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" - btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" - ftypes "github.com/babylonlabs-io/babylon/x/finality/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" - sdkquery "github.com/cosmos/cosmos-sdk/types/query" - sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/relayer/v2/relayer/provider" "github.com/stretchr/testify/require" ) @@ -538,92 +528,6 @@ func (tm *TestManager) Undelegate( return unbondingSlashingInfo, unbondingTxSchnorrSig } -func (tm *TestManager) VoteAndEquivocate(t *testing.T, fpSK *btcec.PrivateKey) { - signerAddr := tm.BabylonClient.MustGetAddr() - - // get the finality provider - fpBTCPK := bbn.NewBIP340PubKeyFromBTCPK(fpSK.PubKey()) - fpResp, err := tm.BabylonClient.FinalityProvider(fpBTCPK.MarshalHex()) - require.NoError(t, err) - btcFp := fpResp.FinalityProvider - - _, err = tm.BabylonClient.ActivatedHeight() - require.Error(t, err) - - activatedHeight := uint64(1) - commitStartHeight := activatedHeight - - /* - commit a number of public randomness since activatedHeight - */ - srList, msgCommitPubRandList, err := datagen.GenRandomMsgCommitPubRandList(r, fpSK, activatedHeight, 100) - require.NoError(t, err) - msgCommitPubRandList.Signer = signerAddr - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgCommitPubRandList, nil, nil) - require.NoError(t, err) - t.Logf("committed public randomness") - - tm.mineBlock(t) - - tm.waitForFpPubRandTimestamped(t, fpSK.PubKey()) - - require.Eventually(t, func() bool { - acr, err := tm.BabylonClient.ActivatedHeight() - if err != nil { - return false - } - activatedHeight = acr.Height - return activatedHeight > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - /* - submit finality signature - */ - // get block to vote - blockToVote, err := tm.BabylonClient.GetBlock(int64(activatedHeight)) - require.NoError(t, err) - msgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), blockToVote.Block.AppHash...) - // generate EOTS signature - idx := activatedHeight - commitStartHeight - sig, err := eots.Sign(fpSK, srList.SRList[idx], msgToSign) - require.NoError(t, err) - eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig) - // submit finality signature - msgAddFinalitySig := &ftypes.MsgAddFinalitySig{ - Signer: signerAddr, - FpBtcPk: btcFp.BtcPk, - BlockHeight: activatedHeight, - PubRand: &srList.PRList[idx], - Proof: srList.ProofList[idx].ToProto(), - BlockAppHash: blockToVote.Block.AppHash, - FinalitySig: eotsSig, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), msgAddFinalitySig, nil, nil) - require.NoError(t, err) - t.Logf("submitted finality signature") - - /* - equivocate - */ - invalidAppHash := datagen.GenRandomByteArray(r, 32) - invalidMsgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), invalidAppHash...) - invalidSig, err := eots.Sign(fpSK, srList.SRList[idx], invalidMsgToSign) - require.NoError(t, err) - invalidEotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(invalidSig) - invalidMsgAddFinalitySig := &ftypes.MsgAddFinalitySig{ - Signer: signerAddr, - FpBtcPk: btcFp.BtcPk, - BlockHeight: activatedHeight, - PubRand: &srList.PRList[idx], - Proof: srList.ProofList[idx].ToProto(), - BlockAppHash: invalidAppHash, - FinalitySig: invalidEotsSig, - } - _, err = tm.BabylonClient.ReliablySendMsg(context.Background(), invalidMsgAddFinalitySig, nil, nil) - require.NoError(t, err) - t.Logf("submitted equivocating finality signature") -} - func getTxInfo(t *testing.T, block *wire.MsgBlock) *btcctypes.TransactionInfo { mHeaderBytes := bbn.NewBTCHeaderBytesFromBlockHeader(&block.Header) var txBytes [][]byte @@ -659,232 +563,6 @@ func outIdx(tx *wire.MsgTx, candOut *wire.TxOut) (uint32, error) { return 0, fmt.Errorf("couldn't find output") } -func (tm *TestManager) waitForFpPubRandTimestamped(t *testing.T, fpPk *btcec.PublicKey) { - var lastCommittedHeight uint64 - var err error - - require.Eventually(t, func() bool { - lastCommittedHeight, err = tm.getLastCommittedHeight(fpPk) - if err != nil { - return false - } - return lastCommittedHeight > 0 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - t.Logf("public randomness is successfully committed, last committed height: %d", lastCommittedHeight) - - // wait until the last registered epoch is finalized - currentEpoch, err := tm.BabylonClient.CurrentEpoch() - require.NoError(t, err) - - tm.finalizeUntilEpoch(t, currentEpoch.CurrentEpoch) - - res, err := tm.BabylonClient.LatestEpochFromStatus(ckpttypes.Finalized) - require.NoError(t, err) - t.Logf("last finalized epoch: %d", res.RawCheckpoint.EpochNum) - - t.Logf("public randomness is successfully timestamped, last finalized epoch: %v", currentEpoch) -} - -// queryLastCommittedPublicRand returns the last public randomness commitments -func (tm *TestManager) queryLastCommittedPublicRand(fpPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { - fpBtcPk := bbn.NewBIP340PubKeyFromBTCPK(fpPk) - - pagination := &sdkquery.PageRequest{ - Limit: count, - Reverse: true, - } - - res, err := tm.BabylonClient.QueryClient.ListPubRandCommit(fpBtcPk.MarshalHex(), pagination) - if err != nil { - return nil, fmt.Errorf("failed to query committed public randomness: %w", err) - } - - return res.PubRandCommitMap, nil -} - -func (tm *TestManager) lastCommittedPublicRandWithRetry(btcPk *btcec.PublicKey, count uint64) (map[uint64]*ftypes.PubRandCommitResponse, error) { - var response map[uint64]*ftypes.PubRandCommitResponse - - if err := retry.Do(func() error { - resp, err := tm.queryLastCommittedPublicRand(btcPk, count) - if err != nil { - return err - } - response = resp - return nil - }, - retry.Attempts(5), - retry.Delay(1*time.Second), - retry.LastErrorOnly(true)); err != nil { - return nil, err - } - - return response, nil -} - -func (tm *TestManager) getLastCommittedHeight(btcPk *btcec.PublicKey) (uint64, error) { - pubRandCommitMap, err := tm.lastCommittedPublicRandWithRetry(btcPk, 1) - if err != nil { - return 0, err - } - - // no committed randomness yet - if len(pubRandCommitMap) == 0 { - return 0, nil - } - - if len(pubRandCommitMap) > 1 { - return 0, fmt.Errorf("got more than one last committed public randomness") - } - var lastCommittedHeight uint64 - for startHeight, resp := range pubRandCommitMap { - lastCommittedHeight = startHeight + resp.NumPubRand - 1 - } - - return lastCommittedHeight, nil -} - -func (tm *TestManager) finalizeUntilEpoch(t *testing.T, epoch uint64) { - bbnClient := tm.BabylonClient - - // wait until the checkpoint of this epoch is sealed - require.Eventually(t, func() bool { - lastSealedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Sealed) - if err != nil { - return false - } - return epoch <= lastSealedCkpt.RawCheckpoint.EpochNum - }, eventuallyWaitTimeOut, 1*time.Second) - - t.Logf("start finalizing epochs until %d", epoch) - // Random source for the generation of BTC data - r := rand.New(rand.NewSource(time.Now().Unix())) - - // get all checkpoints of these epochs - pagination := &sdkquerytypes.PageRequest{ - Key: ckpttypes.CkptsObjectKey(0), - Limit: epoch, - } - resp, err := bbnClient.RawCheckpoints(pagination) - require.NoError(t, err) - require.Equal(t, int(epoch), len(resp.RawCheckpoints)) - - submitterAddr, err := sdk.AccAddressFromBech32(tm.BabylonClient.MustGetAddr()) - require.NoError(t, err) - - for _, checkpoint := range resp.RawCheckpoints { - currentBtcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() - require.NoError(t, err) - tipHeader, err := bbn.NewBTCHeaderBytesFromHex(currentBtcTipResp.Header.HeaderHex) - require.NoError(t, err) - - rawCheckpoint, err := checkpoint.Ckpt.ToRawCheckpoint() - require.NoError(t, err) - - btcCheckpoint, err := ckpttypes.FromRawCkptToBTCCkpt(rawCheckpoint, submitterAddr) - require.NoError(t, err) - - babylonTagBytes, err := hex.DecodeString("01020304") - require.NoError(t, err) - - p1, p2, err := txformat.EncodeCheckpointData( - babylonTagBytes, - txformat.CurrentVersion, - btcCheckpoint, - ) - require.NoError(t, err) - - tx1 := datagen.CreatOpReturnTransaction(r, p1) - - opReturn1 := datagen.CreateBlockWithTransaction(r, tipHeader.ToBlockHeader(), tx1) - tx2 := datagen.CreatOpReturnTransaction(r, p2) - opReturn2 := datagen.CreateBlockWithTransaction(r, opReturn1.HeaderBytes.ToBlockHeader(), tx2) - - // insert headers and proofs - _, err = tm.insertBtcBlockHeaders([]bbn.BTCHeaderBytes{ - opReturn1.HeaderBytes, - opReturn2.HeaderBytes, - }) - require.NoError(t, err) - - _, err = tm.insertSpvProofs(submitterAddr.String(), []*btcctypes.BTCSpvProof{ - opReturn1.SpvProof, - opReturn2.SpvProof, - }) - require.NoError(t, err) - - // wait until this checkpoint is submitted - require.Eventually(t, func() bool { - ckpt, err := bbnClient.RawCheckpoint(checkpoint.Ckpt.EpochNum) - if err != nil { - return false - } - return ckpt.RawCheckpoint.Status == ckpttypes.Submitted - }, eventuallyWaitTimeOut, eventuallyPollTime) - } - - // insert w BTC headers - tm.insertWBTCHeaders(t, r) - - // wait until the checkpoint of this epoch is finalised - require.Eventually(t, func() bool { - lastFinalizedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Finalized) - if err != nil { - t.Logf("failed to get last finalized epoch: %v", err) - return false - } - return epoch <= lastFinalizedCkpt.RawCheckpoint.EpochNum - }, eventuallyWaitTimeOut, 1*time.Second) - - t.Logf("epoch %d is finalised", epoch) -} - -func (tm *TestManager) insertBtcBlockHeaders(headers []bbn.BTCHeaderBytes) (*provider.RelayerTxResponse, error) { - msg := &btclctypes.MsgInsertHeaders{ - Signer: tm.MustGetBabylonSigner(), - Headers: headers, - } - - res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) - if err != nil { - return nil, err - } - - return res, nil -} - -func (tm *TestManager) insertSpvProofs(submitter string, proofs []*btcctypes.BTCSpvProof) (*provider.RelayerTxResponse, error) { - msg := &btcctypes.MsgInsertBTCSpvProof{ - Submitter: submitter, - Proofs: proofs, - } - - res, err := tm.BabylonClient.ReliablySendMsg(context.Background(), msg, nil, nil) - if err != nil { - return nil, err - } - - return res, nil -} - -func (tm *TestManager) insertWBTCHeaders(t *testing.T, r *rand.Rand) { - ckptParamRes, err := tm.BabylonClient.QueryClient.BTCCheckpointParams() - require.NoError(t, err) - btcTipResp, err := tm.BabylonClient.QueryClient.BTCHeaderChainTip() - require.NoError(t, err) - tipHeader, err := bbn.NewBTCHeaderBytesFromHex(btcTipResp.Header.HeaderHex) - require.NoError(t, err) - kHeaders := datagen.NewBTCHeaderChainFromParentInfo(r, &btclctypes.BTCHeaderInfo{ - Header: &tipHeader, - Hash: tipHeader.Hash(), - Height: btcTipResp.Header.Height, - Work: &btcTipResp.Header.Work, - }, uint32(ckptParamRes.Params.CheckpointFinalizationTimeout)) - _, err = tm.insertBtcBlockHeaders(kHeaders.ChainToBytes()) - require.NoError(t, err) -} - func (tm *TestManager) getHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, error) { utxos, err := tm.WalletClient.ListUnspent() if err != nil { From 9fda0ded99aefa423c2b42ad11335960194e7990 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 19 Dec 2024 19:01:45 +0530 Subject: [PATCH 30/32] pr comment: add .db in gitignore --- .gitignore | 5 ++++- e2etest/wallet.db | Bin 196608 -> 0 bytes 2 files changed, 4 insertions(+), 1 deletion(-) delete mode 100644 e2etest/wallet.db diff --git a/.gitignore b/.gitignore index 8de8744..3ccd446 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,7 @@ go.work rabbitmq_data .vscode -.env \ No newline at end of file +.env + +# Database files +*.db \ No newline at end of file diff --git a/e2etest/wallet.db b/e2etest/wallet.db deleted file mode 100644 index c8e7845e5b663b71dd86f588fc22a1fe4f8e6b1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196608 zcmeI*c|28H{|E4s;UIHLDIrDDV2C6cBFR*mOHqd-ncd@@%q3SBl~j~UPlybKQrAT! zN{LcYHEk^J_?>7Jjb*Yn)xdA;uQ$GxBRdab?IcdfPeKKp&P^WS%&P(}*R zXxsimsGW=K8;lGHjx^tnL!(7#bYyrf(cjtFu&_4lR>`-8=s*Ah5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1R(H-1;&l8{BQIBxkt|rRjt%(TwVD358oIIApijgKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5cnp*HQE#VHi8ZWAOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=|7!v6(HpY&0r+3vANPX*1Rwwb2tWV=5P$##AOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG{VLg5)j;Gzf&_r?*&BSa;~bBKV9b%@OY$l2{gCF3X}7UYQAiI`k3 zM^tit9I>eYIZwu9ktSj)(Z&o%6iONq^Nz;kdQIYF@_c=wlJPDgAnzwfRGPg#lW4wM zxG2BxS|CTPAg{5CyZ}+`h?b0J3`fMllo%jCz>u8eretB0X_S|%ZA|GeooA+)wnfWF zU-OgyD3LKwp{CGo#iuhlRqE~fn#ILy9&RgQE<7V|98;{>E*WaS+|X`A5X9G)q!aT zJ1ptSLLnEnpGp$VvsIKX0Th!)Itky!1mzw+dY7qserJXfTs zV=nqugPy6fi2H)%y%qZtvP(k5Uos<`xz-3Jb~o#KJ`s7bZC|3JM0h{dKh}Fwjr8Iu z{brN1H63m4{j1mNUwsvmIk~z|ZlCi5hk#V;W7;8n(b}&Ba@)N8YMl>u=o24~WjF1k zI1j`4-bTd_Hs#06O!ZWPDpDEJ=NG%VJhpRIYw~_l`>}j>l;SkS!d;?sa9VU!AOQ`=k%cs@)uz zwC{ZfJuaTAid}M#0|2%^c2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SNqQ0%RUOS^hTp{}MI<2UN@q7l{X^T>I6xe|yVG zyQ&!qB?H2}>M06Tmr~lbs`In2N4FT7Q9c>VxbHF#@~*n;)B1jk*aEtP_GaOtX%E-x z%(FLkol#b^;>L}~Nt9DzP9L~#&YX2ROnFhMT*S}uLB&cJR&S}H%(C208(JCebbjw9 z!?e?iFU8A#Oh~;?+1!5bZO|{tB3~TR%Fhj&$R9hpqx%Ax|9>#9CD& z#hOr7nMIm+$i!QHvJQ&u8j{!fpfRxbhOG8esrqpSKkF_Q-Jtz(-BbD{!QEAJi*ww~ z^gVQVC*BX9-e+old1lkR#jkH0bPCmQPoByrxSlr8)KcC>wWx7sqjHhFOOe+4UfT@M zLi2w6yKz3r{(XW*`&7p*v`WxCGt+3TvCU#nt7W$wWp{S35eOn*53+D1$0PrLgoNYB z&KLiW^Z$9a_1g8@q}~j&U8%NiS?c5T!urkgw>fQ{;B$0l;a%;Iu9f~5W2#aEx~}_8lFk`t$z#2|g!}#xKzHQ?@=3am*@Qi#znQ zqT#d0JDOeuThFmw)hj>OUTuq2)U4;1uFfh9E;i@=Nv1*A>!+{A4O@jMi!T}|yhv%! z50W@(({Q-6VL1OkSF?Wa8OFrz+c!=2_vD+>-*GC;rSN%9km>79m-HgNZNKpL|4?Ty zQQa_9D55vB=0oFIjnA|7LqZ)+#HGF9vtjg{Z#xy+RS|mc%~KbnlSe9^Ym^+db2dv8 zyu)R5+A97qoo8r%{@(=3?UfvJb~o9GTHVgdFKSJ1Zi+YY zQY^h*uwMIps77oN^8YzrJai!d0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*#kN&?i;hxyy& z|1;(E9aUnCx~V+r2{JRwH~K5Ru~oYAW3HZRy>!y4Cc#BQqQ^9k-*LI4XDxL@dTyt4 zTt}R?MSaQ&$yX~&x8QxZ`?SJux)J zOZ}|swRw5&6-8-byYGL>eJAFe>wEZB?u5lBeqiihCU;srj-gr@kyP77ZOZq|m26Qm zQmM9BQL3})eh)pnBd47A)Ww@;E{R`o)3|lqR;#owc-@2ai?2`EmL)jKTEuy*xqWR% z+^(gq?AIOn|731ITVrhdU+4de52l%0P0aHw`(pV{VedeCs;$0ozr`T4Xm7Nsw4UQR z)e}973g^32oOH347)bD2Bk-F&WJWDEGbBG*?JU+=S%x82euZ@!O3JyuXbFHH8xj61nh z;atn~`8wZqc` zZk6fLB+7&jT%_hX$;4ay<~8@Lt5SEobVf2*^Q4{ckZ9V+!aVIal9I3D$(ytY3M3w&+DAk*;y+r)YulhL~ZxA}$HjKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P-m6Nxv`a({rm#A0%NH&NYaPVPkW z-NHo~|7|>;=#keX_ZA>aEm6s9uwyCW!es70JKjvhtNYY-#b>Ap zG6!Q81=>ZiNTg5BmZ*+!eAnYHy}>*D$BI22_Fnc3Cr@Vb?dADW27?j2$_`{3mYrM#K^rDVChcT&9{b#A?9g8Mx-zjZq zufF0R!D7+N2i^)?zMFOW9qV!-*Ja~mAAXxiA+fXw-Z>9e&P@}4rOrE5`yw?Uz&iib zrv-bi*T4D6c1?HtRNEELy{x$MQ}-!OtF{x~qh0NNm&NR!>TgMYI+k5hH*5WRKV5Bp ztK`fVt`40U1GVp}cf9`48D&r9%~2^58vMX7zUfqDWz*_IXZwFinY(uSs>0Ad#prw; zkx6Fbqo}!^gQw-(eO+=b$J;o}kh*x@^2U?`#j-EQwJcUm{fD~ll{xPZNLzH-;MVVxZl;Gs>aGbX^^?C@K#ap^v^d+_vh|258GCJS8q00Izz00bZa0SG_<0uX=z1Rwwb2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##An=zI zU}xlW4QKR^nY+)9i7w?mQORUmGJ%#H zZy_p~1T05XGX65!vSVRlkaJ#kOsq!4}xPN2RZxf z1uV`%&OUnqu>*h?G0qM-m!J;;2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0)Li( z;OK?9M)SSddG=)Pz8F!-nEWT$)X~_RMyE4~e-kf2#GJ*O=|haNcN5^8K_3DTfB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz i00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOL}XC-8q8p0pkS From 7c2febc68aae85f2dbdb99ea6497a572c614776e Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 19 Dec 2024 19:05:01 +0530 Subject: [PATCH 31/32] pr comment: fix test name --- e2etest/e2e_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2etest/e2e_test.go b/e2etest/e2e_test.go index e541f87..db5425c 100644 --- a/e2etest/e2e_test.go +++ b/e2etest/e2e_test.go @@ -63,7 +63,7 @@ func TestQueueConsumer(t *testing.T) { } } -// TestStakingLifecycle verifies the happy path of BTC delegation state transitions +// TestStakingEarlyUnbonding verifies the following state transitions: // PENDING -> VERIFIED -> ACTIVE -> UNBONDING/EARLY_UNBONDING // 1. Create BTC delegation without inclusion proof in Babylon node (pre-approval flow) // 2. Wait for delegation to be PENDING in Indexer DB @@ -77,7 +77,7 @@ func TestQueueConsumer(t *testing.T) { // 10. Wait for delegation to be UNBONDED in Babylon node // 11. Wait for delegation to be UNBONDING and sub-state to be EARLY_UNBONDING in Indexer DB // 12. Verify unbonding staking event emitted by Indexer -func TestStakingLifecycle(t *testing.T) { +func TestStakingEarlyUnbonding(t *testing.T) { // Segw is activated at height 300. It's necessary for staking/slashing tx numMatureOutputs := uint32(300) ctx := context.Background() From 8a667c8510922e32cad39dcce44f554da017993a Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 19 Dec 2024 19:11:55 +0530 Subject: [PATCH 32/32] pr comment: move utxo types inside e2etest/ --- e2etest/test_manager_btcstaking.go | 2 +- {internal => e2etest}/types/utxo.go | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {internal => e2etest}/types/utxo.go (100%) diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index cb24039..de445f9 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -9,7 +9,7 @@ import ( "time" sdkmath "cosmossdk.io/math" - "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" + "github.com/babylonlabs-io/babylon-staking-indexer/e2etest/types" "github.com/babylonlabs-io/babylon/btcstaking" asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" "github.com/babylonlabs-io/babylon/testutil/datagen" diff --git a/internal/types/utxo.go b/e2etest/types/utxo.go similarity index 100% rename from internal/types/utxo.go rename to e2etest/types/utxo.go