From d931b2c30466c55a966095cf42bb282af883bfe3 Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Wed, 7 Aug 2024 16:49:53 +0200 Subject: [PATCH 01/12] switch to gh actions (#5) --- .circleci/config.yml | 105 ---------------------------------- .github/workflows/ci.yml | 21 +++++++ .github/workflows/publish.yml | 25 ++++++++ 3 files changed, 46 insertions(+), 105 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 7f90a64e..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,105 +0,0 @@ -version: 2.1 - -orbs: - go: circleci/go@1.9.0 - aws-ecr: circleci/aws-ecr@8.2.1 - -jobs: - build_lint_test: - machine: - image: ubuntu-2204:2022.10.1 - resource_class: large - steps: - - go/install: - version: "1.21.4" - - checkout - - run: - name: Print Go environment - command: "go env" - - go/load-cache: - key: go-mod-v6-{{ checksum "go.sum" }} - - go/mod-download - - run: - name: Install Dependencies - command: sudo apt-get install libzmq3-dev - - go/save-cache: - key: go-mod-v6-{{ checksum "go.sum" }} - path: "/home/circleci/.go_workspace/pkg/mod" - - run: - name: Build vigilante - command: make build - - run: - name: Lint - command: | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.55.2 - ./bin/golangci-lint run - - run: - name: Run tests - command: | - make test - - run: - name: Run integration tests - command: | - make test-e2e - - build_docker: - machine: - image: ubuntu-2204:2022.10.1 - resource_class: large - steps: - - checkout - - aws-ecr/build-image: - push-image: false - dockerfile: Dockerfile - path: ./ - build-path: ./ - tag: "$CIRCLE_SHA1,$CIRCLE_TAG" - repo: "vigilante" - - run: - name: Save Docker image to export it to workspace - command: | - docker save $(docker image ls --format '{{.Repository}}:{{.Tag}}') > /tmp/vigilante.tar - - persist_to_workspace: - root: /tmp - paths: - - vigilante.tar - - push_docker: - machine: - image: ubuntu-2204:2022.10.1 - resource_class: large - steps: - - attach_workspace: - at: /tmp - - run: - name: Load Docker image from workspace - command: | - docker load -i /tmp/vigilante.tar - - aws-ecr/ecr-login: - aws-access-key-id: AWS_ACCESS_KEY_ID - aws-secret-access-key: AWS_SECRET_ACCESS_KEY - region: "$AWS_REGION" - - aws-ecr/push-image: - registry-id: AWS_ECR_REGISTRY_ID - region: "$AWS_REGION" - repo: "vigilante" - tag: "$CIRCLE_SHA1,$CIRCLE_TAG" - -workflows: - CI: - jobs: - - build_lint_test - - build_docker: - filters: - tags: - only: /.*/ - - push_docker: - requires: - - build_docker - filters: - tags: - only: /.*/ - branches: - only: - - main - - dev diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..4d1c9f0f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: ci + +on: + pull_request: + branches: + - '**' + +jobs: + lint_test: + uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.3.1 + with: + run-unit-tests: true + run-integration-tests: true + run-lint: true + install-dependencies-command: sudo apt-get install libzmq3-dev + + docker_pipeline: + uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.3.1 + secrets: inherit + with: + publish: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..8af43ef7 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,25 @@ +name: docker_publish + +on: + push: + branches: + - 'main' + - 'dev' + tags: + - '*' + +jobs: + lint_test: + uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.3.1 + with: + run-unit-tests: true + run-integration-tests: true + run-lint: true + install-dependencies-command: sudo apt-get install libzmq3-dev + + docker_pipeline: + needs: ["lint_test"] + uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.3.1 + secrets: inherit + with: + publish: true From 09510cc67eb288af5a7ab2a3a8de5bf373ac06d4 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:54:36 +0200 Subject: [PATCH 02/12] chore(e2e): removing btcd (#9) This PR removes btcd for running e2e tests and uses bitcoind. Reference [issue](https://github.com/babylonchain/vigilante/issues/226) --------- Co-authored-by: Runchao Han Co-authored-by: KonradStaniec --- .github/workflows/ci.yml | 4 +- Makefile | 5 +- btcclient/client.go | 6 + btcclient/client_block_subscriber.go | 9 - btcclient/notifier.go | 3 +- config/bitcoin.go | 2 +- e2etest/atomicslasher_e2e_test.go | 136 ++++------- e2etest/babylon_node_handler.go | 7 +- e2etest/bitcoind_node_setup.go | 110 +++++++++ e2etest/btcd_wallet_handler.go | 179 --------------- e2etest/container/config.go | 23 ++ e2etest/container/container.go | 188 +++++++++++++++ e2etest/reporter_e2e_test.go | 135 ++++------- e2etest/slasher_e2e_test.go | 198 ++++------------ e2etest/submitter_e2e_test.go | 110 ++++----- e2etest/test_manager.go | 326 +++++++-------------------- e2etest/test_manager_btcstaking.go | 50 ++-- e2etest/unbondingwatcher_e2e_test.go | 59 ++--- go.mod | 16 ++ go.sum | 46 ++++ tools/go.mod | 2 - tools/tools.go | 2 - 22 files changed, 709 insertions(+), 907 deletions(-) create mode 100644 e2etest/bitcoind_node_setup.go delete mode 100644 e2etest/btcd_wallet_handler.go create mode 100644 e2etest/container/config.go create mode 100644 e2etest/container/container.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d1c9f0f..8dc848bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,9 @@ jobs: run-unit-tests: true run-integration-tests: true run-lint: true - install-dependencies-command: sudo apt-get install libzmq3-dev + install-dependencies-command: | + sudo apt-get update + sudo apt-get install -y libzmq3-dev docker_pipeline: uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.3.1 diff --git a/Makefile b/Makefile index ad9d324d..dd9d0f8b 100644 --- a/Makefile +++ b/Makefile @@ -6,12 +6,9 @@ MOCKGEN_CMD=go run ${MOCKGEN_REPO}@${MOCKGEN_VERSION} BUILDDIR ?= $(CURDIR)/build TOOLS_DIR := tools -BTCD_PKG := github.com/btcsuite/btcd -BTCDW_PKG := github.com/btcsuite/btcwallet BABYLON_PKG := github.com/babylonlabs-io/babylon/cmd/babylond GO_BIN := ${GOPATH}/bin -BTCD_BIN := $(GO_BIN)/btcd ldflags := $(LDFLAGS) build_tags := $(BUILD_TAGS) @@ -52,7 +49,7 @@ test: go test ./... test-e2e: - cd $(TOOLS_DIR); go install -trimpath $(BTCD_PKG); go install -trimpath $(BTCDW_PKG); go install -trimpath $(BABYLON_PKG); + cd $(TOOLS_DIR); go install -trimpath $(BABYLON_PKG); go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -count=1 --tags=e2e build-docker: diff --git a/btcclient/client.go b/btcclient/client.go index b784fd9b..4cd3933f 100644 --- a/btcclient/client.go +++ b/btcclient/client.go @@ -60,4 +60,10 @@ func (c *Client) Stop() { if c.blockEventChan != nil { close(c.blockEventChan) } + + if c.zmqClient != nil { + if err := c.zmqClient.Close(); err != nil { + c.logger.Debug(err) + } + } } diff --git a/btcclient/client_block_subscriber.go b/btcclient/client_block_subscriber.go index f0926a01..c3245158 100644 --- a/btcclient/client_block_subscriber.go +++ b/btcclient/client_block_subscriber.go @@ -51,15 +51,6 @@ func NewWithBlockSubscriber(cfg *config.BTCConfig, retrySleepTime, maxRetrySleep return nil, err } - // ensure we are using bitcoind as Bitcoin node, as zmq is only supported by bitcoind - backend, err := rpcClient.BackendVersion() - if err != nil { - return nil, fmt.Errorf("failed to get BTC backend: %v", err) - } - if backend != rpcclient.BitcoindPost25 { - return nil, fmt.Errorf("zmq is only supported by bitcoind, but got %v", backend) - } - zmqClient, err := zmq.New(logger, cfg.ZmqSeqEndpoint, client.blockEventChan, rpcClient) if err != nil { return nil, err diff --git a/btcclient/notifier.go b/btcclient/notifier.go index 2e31fd71..4bc73c71 100644 --- a/btcclient/notifier.go +++ b/btcclient/notifier.go @@ -48,7 +48,7 @@ func DefaultBitcoindConfig() Bitcoind { RPCHost: config.DefaultRpcBtcNodeHost, RPCUser: config.DefaultBtcNodeRpcUser, RPCPass: config.DefaultBtcNodeRpcPass, - RPCPolling: false, + RPCPolling: true, BlockPollingInterval: 30 * time.Second, TxPollingInterval: 30 * time.Second, EstimateMode: config.DefaultBtcNodeEstimateMode, @@ -81,7 +81,6 @@ func CfgToBtcNodeBackendConfig(cfg config.BTCConfig, rawCert string) *BtcNodeBac ActiveNodeBackend: types.Bitcoind, Bitcoind: &defaultBitcoindCfg, } - case types.Btcd: return &BtcNodeBackendConfig{ ActiveNodeBackend: types.Btcd, diff --git a/config/bitcoin.go b/config/bitcoin.go index c188dd14..aa2b5e97 100644 --- a/config/bitcoin.go +++ b/config/bitcoin.go @@ -102,7 +102,7 @@ const ( DefaultBtcNodeRpcPass = "rpcpass" DefaultBtcNodeEstimateMode = "CONSERVATIVE" DefaultBtcblockCacheSize = 20 * 1024 * 1024 // 20 MB - DefaultZmqSeqEndpoint = "tcp://127.0.0.1:29000" + DefaultZmqSeqEndpoint = "tcp://127.0.0.1:28333" DefaultZmqBlockEndpoint = "tcp://127.0.0.1:29001" DefaultZmqTxEndpoint = "tcp://127.0.0.1:29002" ) diff --git a/e2etest/atomicslasher_e2e_test.go b/e2etest/atomicslasher_e2e_test.go index d30181ac..c9799026 100644 --- a/e2etest/atomicslasher_e2e_test.go +++ b/e2etest/atomicslasher_e2e_test.go @@ -4,7 +4,7 @@ package e2etest import ( - "encoding/hex" + "go.uber.org/zap" "testing" "time" @@ -14,44 +14,22 @@ import ( "github.com/babylonlabs-io/vigilante/btcstaking-tracker/btcslasher" "github.com/babylonlabs-io/vigilante/config" "github.com/babylonlabs-io/vigilante/metrics" - "github.com/babylonlabs-io/vigilante/types" - "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" ) +// 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) { // segwit is activated at height 300. It's needed by staking/slashing tx numMatureOutputs := uint32(300) - submittedTxs := map[chainhash.Hash]struct{}{} - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - log.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - log.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTxs[*hash] = struct{}{} - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) - err = tm.MinerNode.Client.NotifyBlocks() - require.NoError(t, err) + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) + // start WebSocket connection with Babylon for subscriber services - err = tm.BabylonClient.Start() + err := tm.BabylonClient.Start() require.NoError(t, err) // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) @@ -61,8 +39,8 @@ func TestAtomicSlasher(t *testing.T) { // TODO: our config only support btcd wallet tls, not btcd directly tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, hex.EncodeToString(tm.MinerNode.RPCConfig().Certificates)), - &chaincfg.SimNetParams, + btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + &chaincfg.RegressionNetParams, &emptyHintCache, ) require.NoError(t, err) @@ -73,9 +51,6 @@ func TestAtomicSlasher(t *testing.T) { commonCfg := config.DefaultCommonConfig() bstCfg := config.DefaultBTCStakingTrackerConfig() bstCfg.CheckDelegationsInterval = 1 * time.Second - logger, err := config.NewRootLogger("auto", "debug") - require.NoError(t, err) - metrics := metrics.NewBTCStakingTrackerMetrics() bsTracker := bst.NewBTCSTakingTracker( @@ -84,15 +59,12 @@ func TestAtomicSlasher(t *testing.T) { tm.BabylonClient, &bstCfg, &commonCfg, - logger, + zap.NewNop(), metrics, ) go bsTracker.Start() defer bsTracker.Stop() - // wait for bootstrapping - time.Sleep(5 * time.Second) - bsParamsResp, err := tm.BabylonClient.BTCStakingParams() require.NoError(t, err) bsParams := bsParamsResp.Params @@ -113,23 +85,25 @@ func TestAtomicSlasher(t *testing.T) { finality provider builds slashing tx witness and sends slashing tx to Bitcoin */ victimBTCDel := btcDels[0] - victimSlashingTx, err := btcslasher.BuildSlashingTxWithWitness(victimBTCDel, &bsParams, netParams, fpSK) + 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 - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash})) - // ensure slashing tx will be detected on Bitcoin require.Eventually(t, func() bool { - _, ok := submittedTxs[*slashingTxHash] - return ok + 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 @@ -149,49 +123,33 @@ func TestAtomicSlasher(t *testing.T) { slashTx2, err := bstypes.NewBTCSlashingTxFromHex(btcDel2.SlashingTxHex) require.NoError(t, err) slashingTxHash2 := slashTx2.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 - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash2})) - // ensure slashing tx 2 will be detected on Bitcoin require.Eventually(t, func() bool { - _, ok := submittedTxs[*slashingTxHash2] - return ok + 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) { // segwit is activated at height 300. It's needed by staking/slashing tx numMatureOutputs := uint32(300) - submittedTxs := map[chainhash.Hash]struct{}{} - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - log.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - log.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTxs[*hash] = struct{}{} - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) - err = tm.MinerNode.Client.NotifyBlocks() - require.NoError(t, err) + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) + // start WebSocket connection with Babylon for subscriber services - err = tm.BabylonClient.Start() + err := tm.BabylonClient.Start() require.NoError(t, err) // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) @@ -201,8 +159,8 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { // TODO: our config only support btcd wallet tls, not btcd directly tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, hex.EncodeToString(tm.MinerNode.RPCConfig().Certificates)), - &chaincfg.SimNetParams, + btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + &chaincfg.RegressionNetParams, &emptyHintCache, ) require.NoError(t, err) @@ -213,10 +171,8 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { commonCfg := config.DefaultCommonConfig() bstCfg := config.DefaultBTCStakingTrackerConfig() bstCfg.CheckDelegationsInterval = 1 * time.Second - logger, err := config.NewRootLogger("auto", "debug") - require.NoError(t, err) - metrics := metrics.NewBTCStakingTrackerMetrics() + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() bsTracker := bst.NewBTCSTakingTracker( tm.BTCClient, @@ -224,15 +180,12 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { tm.BabylonClient, &bstCfg, &commonCfg, - logger, - metrics, + zap.NewNop(), + stakingTrackerMetrics, ) go bsTracker.Start() defer bsTracker.Stop() - // wait for bootstrapping - time.Sleep(5 * time.Second) - bsParamsResp, err := tm.BabylonClient.BTCStakingParams() require.NoError(t, err) bsParams := bsParamsResp.Params @@ -252,6 +205,7 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { 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 @@ -262,6 +216,8 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { } } + require.NotNil(t, btcDel2, "err second delegation not found") + /* the victim BTC delegation unbonds */ @@ -270,8 +226,9 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { /* finality provider builds unbonding slashing tx witness and sends it to Bitcoin */ - victimUnbondingSlashingTx, err := btcslasher.BuildUnbondingSlashingTxWithWitness(victimBTCDel, &bsParams, netParams, fpSK) + 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 @@ -283,6 +240,7 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { } return true }, eventuallyWaitTimeOut, eventuallyPollTime) + // unbonding slashing tx is eventually queryable require.Eventually(t, func() bool { _, err := tm.BTCClient.GetRawTransaction(unbondingSlashingTxHash) @@ -293,13 +251,13 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { return true }, eventuallyWaitTimeOut, eventuallyPollTime) // mine a block that includes unbonding slashing tx, which will trigger atomic slasher - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingSlashingTxHash})) - // ensure unbonding slashing tx will be detected on Bitcoin require.Eventually(t, func() bool { - _, ok := submittedTxs[*unbondingSlashingTxHash] - return ok + 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 @@ -317,17 +275,19 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { */ 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 - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingTxHash2})) - // ensure slashing tx 2 will be detected on Bitcoin require.Eventually(t, func() bool { - _, ok := submittedTxs[*slashingTxHash2] - return ok + 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/babylon_node_handler.go b/e2etest/babylon_node_handler.go index 0590047a..e77f623a 100644 --- a/e2etest/babylon_node_handler.go +++ b/e2etest/babylon_node_handler.go @@ -22,7 +22,7 @@ var ( func baseDirBabylondir() (string, error) { tempPath := os.TempDir() - tempName, err := os.MkdirTemp(tempPath, "zBabylonTestVigilante") + tempName, err := os.MkdirTemp(tempPath, "BabylonTestVigilante") if err != nil { return "", err } @@ -123,7 +123,7 @@ type BabylonNodeHandler struct { babylonNode *babylonNode } -func NewBabylonNodeHandler() (*BabylonNodeHandler, error) { +func NewBabylonNodeHandler(baseHeaderHex string, slashingAddress string) (*BabylonNodeHandler, error) { testDir, err := baseDirBabylondir() if err != nil { return nil, err @@ -140,6 +140,9 @@ func NewBabylonNodeHandler() (*BabylonNodeHandler, error) { "--btc-finalization-timeout=4", "--btc-confirmation-depth=2", "--additional-sender-account", + "--btc-network=regtest", + fmt.Sprintf("--slashing-address=%s", slashingAddress), + fmt.Sprintf("--btc-base-header=%s", baseHeaderHex), "--covenant-quorum=1", fmt.Sprintf("--covenant-pks=%s", bbn.NewBIP340PubKeyFromBTCPK(juryPK).MarshalHex()), ) diff --git a/e2etest/bitcoind_node_setup.go b/e2etest/bitcoind_node_setup.go new file mode 100644 index 00000000..52ffb4d4 --- /dev/null +++ b/e2etest/bitcoind_node_setup.go @@ -0,0 +1,110 @@ +package e2etest + +import ( + "encoding/json" + "fmt" + "github.com/babylonlabs-io/vigilante/e2etest/container" + "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) *BitcoindTestHandler { + manager, err := container.NewManager() + require.NoError(t, err) + + return &BitcoindTestHandler{ + t: t, + m: manager, + } +} + +func (h *BitcoindTestHandler) Start() { + tempPath, err := os.MkdirTemp("", "vigilante-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() + if err != nil { + 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 + } + + parsedBuffStr := strings.TrimSuffix(buff.String(), "\n") + + return strconv.Atoi(parsedBuffStr) +} + +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 +} + +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 +} + +// 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) +} diff --git a/e2etest/btcd_wallet_handler.go b/e2etest/btcd_wallet_handler.go deleted file mode 100644 index 52a97813..00000000 --- a/e2etest/btcd_wallet_handler.go +++ /dev/null @@ -1,179 +0,0 @@ -package e2etest - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" -) - -func baseDir() (string, error) { - dirPath := filepath.Join(os.TempDir(), "btcdwallet", "rpctest") - err := os.MkdirAll(dirPath, 0755) - return dirPath, err -} - -type wallet struct { - cmd *exec.Cmd - pidFile string - dataDir string -} - -func newWallet(dataDir string, cmd *exec.Cmd) *wallet { - return &wallet{ - dataDir: dataDir, - cmd: cmd, - } -} - -func (n *wallet) start() error { - if err := n.cmd.Start(); err != nil { - return err - } - - pid, err := os.Create(filepath.Join(n.dataDir, - fmt.Sprintf("%s.pid", "config"))) - if err != nil { - return err - } - - n.pidFile = pid.Name() - if _, err = fmt.Fprintf(pid, "%d\n", n.cmd.Process.Pid); err != nil { - return err - } - - if err := pid.Close(); err != nil { - return err - } - - return nil -} - -func (n *wallet) stop() (err error) { - if n.cmd == nil || n.cmd.Process == nil { - // return if not properly initialized - // or error starting the process - return nil - } - - defer func() { - err = n.cmd.Wait() - }() - - if runtime.GOOS == "windows" { - return n.cmd.Process.Signal(os.Kill) - } - return n.cmd.Process.Signal(os.Interrupt) -} - -func (n *wallet) cleanup() error { - if n.pidFile != "" { - if err := os.Remove(n.pidFile); err != nil { - log.Errorf("unable to remove file %s: %v", n.pidFile, - err) - } - } - - dirs := []string{ - n.dataDir, - } - var err error - for _, dir := range dirs { - if err = os.RemoveAll(dir); err != nil { - log.Errorf("Cannot remove dir %s: %v", dir, err) - } - } - return err -} - -func (n *wallet) shutdown() error { - if err := n.stop(); err != nil { - return err - } - if err := n.cleanup(); err != nil { - return err - } - return nil -} - -type WalletHandler struct { - wallet *wallet -} - -func NewWalletHandler(btcdCert []byte, walletPath string, btcdHost string) (*WalletHandler, error) { - testDir, err := baseDir() - logsPath := filepath.Join(testDir, "logs") - walletDir := filepath.Join(testDir, "simnet") - - if err := os.Mkdir(walletDir, os.ModePerm); err != nil { - return nil, err - } - - if err != nil { - return nil, err - } - - certFile := filepath.Join(testDir, "rpc.cert") - - dir := fmt.Sprintf("--appdata=%s", testDir) - logDir := fmt.Sprintf("--logdir=%s", logsPath) - certDir := fmt.Sprintf("--cafile=%s", certFile) - hostConf := fmt.Sprintf("--rpcconnect=%s", btcdHost) - - // Write cert and key files. - if err = os.WriteFile(certFile, btcdCert, 0666); err != nil { - return nil, err - } - - tempWalletPath := filepath.Join(walletDir, "wallet.db") - - // Read all content of src to data, may cause OOM for a large file. - data, err := os.ReadFile(walletPath) - - if err != nil { - return nil, err - } - - // Write data to dst - err = os.WriteFile(tempWalletPath, data, 0644) - - if err != nil { - return nil, err - } - - // btcwallet --btcdusername=user --btcdpassword=pass --username=user --password=pass --noinitialload --noservertls --simnet - createCmd := exec.Command( - "btcwallet", - "--debuglevel=debug", - "--btcdusername=user", - "--btcdpassword=pass", - "--username=user", - "--password=pass", - "--noservertls", - "--simnet", - hostConf, - certDir, - dir, - logDir, - ) - - return &WalletHandler{ - wallet: newWallet(testDir, createCmd), - }, nil -} - -func (w *WalletHandler) Start() error { - if err := w.wallet.start(); err != nil { - return w.wallet.cleanup() - } - return nil -} - -func (w *WalletHandler) Stop() error { - if err := w.wallet.shutdown(); err != nil { - return err - } - - return nil -} diff --git a/e2etest/container/config.go b/e2etest/container/config.go new file mode 100644 index 00000000..b1d14313 --- /dev/null +++ b/e2etest/container/config.go @@ -0,0 +1,23 @@ +package container + +// 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/e2etest/container/container.go b/e2etest/container/container.go new file mode 100644 index 00000000..c95bd19b --- /dev/null +++ b/e2etest/container/container.go @@ -0,0 +1,188 @@ +package container + +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{ + "-debug=1", + "-regtest", + "-txindex", + "-rpcuser=user", + "-rpcpassword=pass", + "-rpcallowip=0.0.0.0/0", + "-rpcbind=0.0.0.0", + "-zmqpubsequence=tcp://0.0.0.0:28333", + }, + }, + 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/e2etest/reporter_e2e_test.go b/e2etest/reporter_e2e_test.go index 142fe3a6..b269832c 100644 --- a/e2etest/reporter_e2e_test.go +++ b/e2etest/reporter_e2e_test.go @@ -4,18 +4,12 @@ package e2etest import ( - "math/rand" + "sync" "testing" "time" - "github.com/babylonlabs-io/babylon/testutil/datagen" "github.com/babylonlabs-io/vigilante/metrics" "github.com/babylonlabs-io/vigilante/reporter" - "github.com/babylonlabs-io/vigilante/types" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/integration/rpctest" - "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/wire" "github.com/stretchr/testify/require" ) @@ -24,90 +18,48 @@ var ( ) func (tm *TestManager) BabylonBTCChainMatchesBtc(t *testing.T) bool { - tipHash, tipHeight, err := tm.BTCClient.GetBestBlock() + 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 uint64(tipHeight) == bbnBtcLcTip.Header.Height && tipHash.String() == bbnBtcLcTip.Header.HashHex -} -func (tm *TestManager) GenerateAndSubmitsNBlocksFromTip(N int) { - var ut time.Time - - for i := 0; i < N; i++ { - tm.MinerNode.GenerateAndSubmitBlock(nil, -1, ut) - } + return uint64(tipHeight) == bbnBtcLcTip.Header.Height && tipHash.String() == bbnBtcLcTip.Header.HashHex } func (tm *TestManager) GenerateAndSubmitBlockNBlockStartingFromDepth(t *testing.T, N int, depth uint32) { - r := rand.New(rand.NewSource(time.Now().Unix())) - if depth == 0 { // depth 0 means we are starting from tip - tm.GenerateAndSubmitsNBlocksFromTip(N) + tm.BitcoindHandler.GenerateBlocks(N) return } - _, bestHeight, err := tm.MinerNode.Client.GetBestBlock() - require.NoError(t, err) - - startingBlockHeight := bestHeight - int32(depth) - - blockHash, err := tm.MinerNode.Client.GetBlockHash(int64(startingBlockHeight)) - require.NoError(t, err) - - startingBlockMsg, err := tm.MinerNode.Client.GetBlock(blockHash) + height, err := tm.TestRpcClient.GetBlockCount() require.NoError(t, err) - startingBlock := btcutil.NewBlock(startingBlockMsg) - startingBlock.SetHeight(startingBlockHeight) + startingBlockHeight := height - int64(depth) - arr := datagen.GenRandomByteArray(r, 20) - add, err := btcutil.NewAddressScriptHashFromHash(arr, tm.MinerNode.ActiveNet) + blockHash, err := tm.TestRpcClient.GetBlockHash(startingBlockHeight) require.NoError(t, err) - var lastSubmittedBlock *btcutil.Block - var ut time.Time + // invalidate blocks from this height + tm.BitcoindHandler.InvalidateBlock(blockHash.String()) for i := 0; i < N; i++ { - var blockToSubmit *btcutil.Block - - if lastSubmittedBlock == nil { - // first block to submit start from starting block - newBlock, err := rpctest.CreateBlock(startingBlock, nil, rpctest.BlockVersion, - ut, add, nil, tm.MinerNode.ActiveNet) - require.NoError(t, err) - blockToSubmit = newBlock - } else { - newBlock, err := rpctest.CreateBlock(lastSubmittedBlock, nil, rpctest.BlockVersion, - ut, add, nil, tm.MinerNode.ActiveNet) - require.NoError(t, err) - blockToSubmit = newBlock - } - err = tm.MinerNode.Client.SubmitBlock(blockToSubmit, nil) - require.NoError(t, err) - lastSubmittedBlock = blockToSubmit + tm.BitcoindHandler.GenerateBlocks(N) } } func TestReporter_BoostrapUnderFrequentBTCHeaders(t *testing.T) { // no need to much mature outputs, we are not going to submit transactions in this test - numMatureOutputs := uint32(2) - - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - } + numMatureOutputs := uint32(150) - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) reporterMetrics := metrics.NewReporterMetrics() + vigilantReporter, err := reporter.New( &tm.Config.Reporter, logger, @@ -120,15 +72,26 @@ func TestReporter_BoostrapUnderFrequentBTCHeaders(t *testing.T) { 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) - for range ticker.C { - tm.GenerateAndSubmitsNBlocksFromTip(1) + defer ticker.Stop() + for { + select { + case <-ticker.C: + tm.BitcoindHandler.GenerateBlocks(1) + case <-stopChan: + return + } } }() // mine some BTC headers - tm.GenerateAndSubmitsNBlocksFromTip(2) + tm.BitcoindHandler.GenerateBlocks(1) // start reporter vigilantReporter.Start() @@ -138,23 +101,16 @@ func TestReporter_BoostrapUnderFrequentBTCHeaders(t *testing.T) { require.Eventually(t, func() bool { return tm.BabylonBTCChainMatchesBtc(t) }, longEventuallyWaitTimeOut, eventuallyPollTime) + + close(stopChan) + wg.Wait() } func TestRelayHeadersAndHandleRollbacks(t *testing.T) { // no need to much mature outputs, we are not going to submit transactions in this test - numMatureOutputs := uint32(2) - - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - } + numMatureOutputs := uint32(150) - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) + tm := StartManager(t, numMatureOutputs) // this is necessary to receive notifications about new transactions entering mempool defer tm.Stop(t) @@ -170,6 +126,7 @@ func TestRelayHeadersAndHandleRollbacks(t *testing.T) { reporterMetrics, ) require.NoError(t, err) + vigilantReporter.Start() defer vigilantReporter.Stop() @@ -178,7 +135,7 @@ func TestRelayHeadersAndHandleRollbacks(t *testing.T) { }, longEventuallyWaitTimeOut, eventuallyPollTime) // generate 3, we are submitting headers 1 by 1 so we use small amount as this is slow process - tm.GenerateAndSubmitsNBlocksFromTip(3) + tm.BitcoindHandler.GenerateBlocks(3) require.Eventually(t, func() bool { return tm.BabylonBTCChainMatchesBtc(t) @@ -195,19 +152,9 @@ func TestRelayHeadersAndHandleRollbacks(t *testing.T) { func TestHandleReorgAfterRestart(t *testing.T) { // no need to much mature outputs, we are not going to submit transactions in this test - numMatureOutputs := uint32(2) - - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - } + numMatureOutputs := uint32(150) - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) + tm := StartManager(t, numMatureOutputs) // this is necessary to receive notifications about new transactions entering mempool defer tm.Stop(t) @@ -243,11 +190,14 @@ func TestHandleReorgAfterRestart(t *testing.T) { // // 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, - tm.BTCClient, + btcClient, tm.BabylonClient, tm.Config.Common.RetrySleepTime, tm.Config.Common.MaxRetrySleepTime, @@ -261,5 +211,4 @@ func TestHandleReorgAfterRestart(t *testing.T) { 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 index c83ae3e4..b9a94e3f 100644 --- a/e2etest/slasher_e2e_test.go +++ b/e2etest/slasher_e2e_test.go @@ -4,7 +4,7 @@ package e2etest import ( - "encoding/hex" + "go.uber.org/zap" "testing" "time" @@ -14,39 +14,14 @@ import ( bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" "github.com/babylonlabs-io/vigilante/config" "github.com/babylonlabs-io/vigilante/metrics" - "github.com/babylonlabs-io/vigilante/types" - "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/wire" "github.com/stretchr/testify/require" ) func TestSlasher_GracefulShutdown(t *testing.T) { - numMatureOutputs := uint32(5) - - submittedTxs := []*chainhash.Hash{} - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - log.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - log.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTxs = append(submittedTxs, hash) - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) - err = tm.MinerNode.Client.NotifyBlocks() - require.NoError(t, err) + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) @@ -56,8 +31,8 @@ func TestSlasher_GracefulShutdown(t *testing.T) { // TODO: our config only support btcd wallet tls, not btcd directly tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, hex.EncodeToString(tm.MinerNode.RPCConfig().Certificates)), - &chaincfg.SimNetParams, + btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + &chaincfg.RegressionNetParams, &emptyHintCache, ) require.NoError(t, err) @@ -68,10 +43,8 @@ func TestSlasher_GracefulShutdown(t *testing.T) { commonCfg := config.DefaultCommonConfig() bstCfg := config.DefaultBTCStakingTrackerConfig() bstCfg.CheckDelegationsInterval = 1 * time.Second - logger, err := config.NewRootLogger("auto", "debug") - require.NoError(t, err) - metrics := metrics.NewBTCStakingTrackerMetrics() + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() bsTracker := bst.NewBTCSTakingTracker( tm.BTCClient, @@ -79,8 +52,8 @@ func TestSlasher_GracefulShutdown(t *testing.T) { tm.BabylonClient, &bstCfg, &commonCfg, - logger, - metrics, + zap.NewNop(), + stakingTrackerMetrics, ) go bsTracker.Start() @@ -88,6 +61,7 @@ func TestSlasher_GracefulShutdown(t *testing.T) { // wait for bootstrapping time.Sleep(10 * time.Second) + tm.BTCClient.Stop() // gracefully shut down defer bsTracker.Stop() } @@ -96,31 +70,10 @@ func TestSlasher_Slasher(t *testing.T) { // segwit is activated at height 300. It's needed by staking/slashing tx numMatureOutputs := uint32(300) - submittedTxs := []*chainhash.Hash{} - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - log.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - log.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTxs = append(submittedTxs, hash) - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) - err = tm.MinerNode.Client.NotifyBlocks() - require.NoError(t, err) + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) // start WebSocket connection with Babylon for subscriber services - err = tm.BabylonClient.Start() + err := tm.BabylonClient.Start() require.NoError(t, err) // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) @@ -130,8 +83,8 @@ func TestSlasher_Slasher(t *testing.T) { // TODO: our config only support btcd wallet tls, not btcd directly tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, hex.EncodeToString(tm.MinerNode.RPCConfig().Certificates)), - &chaincfg.SimNetParams, + btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + &chaincfg.RegressionNetParams, &emptyHintCache, ) require.NoError(t, err) @@ -142,10 +95,7 @@ func TestSlasher_Slasher(t *testing.T) { commonCfg := config.DefaultCommonConfig() bstCfg := config.DefaultBTCStakingTrackerConfig() bstCfg.CheckDelegationsInterval = 1 * time.Second - logger, err := config.NewRootLogger("auto", "debug") - require.NoError(t, err) - - metrics := metrics.NewBTCStakingTrackerMetrics() + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() bsTracker := bst.NewBTCSTakingTracker( tm.BTCClient, @@ -153,8 +103,8 @@ func TestSlasher_Slasher(t *testing.T) { tm.BabylonClient, &bstCfg, &commonCfg, - logger, - metrics, + zap.NewNop(), + stakingTrackerMetrics, ) go bsTracker.Start() defer bsTracker.Stop() @@ -175,49 +125,25 @@ func TestSlasher_Slasher(t *testing.T) { require.NoError(t, err) slashingMsgTxHash1 := slashingMsgTx.TxHash() slashingMsgTxHash := &slashingMsgTxHash1 - // slashing tx will eventually enter mempool - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(slashingMsgTxHash) - t.Logf("err of getting slashingMsgTxHash: %v", err) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) + // mine a block that includes slashing tx - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingMsgTxHash})) - // ensure 2 txs will eventually be received (staking tx and slashing tx) require.Eventually(t, func() bool { - return len(submittedTxs) == 2 + 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) { // segwit is activated at height 300. It's needed by staking/slashing tx numMatureOutputs := uint32(300) - submittedTxs := []*chainhash.Hash{} - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - log.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - log.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTxs = append(submittedTxs, hash) - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) - err = tm.MinerNode.Client.NotifyBlocks() - require.NoError(t, err) + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) // start WebSocket connection with Babylon for subscriber services - err = tm.BabylonClient.Start() + err := tm.BabylonClient.Start() require.NoError(t, err) // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) @@ -227,8 +153,8 @@ func TestSlasher_SlashingUnbonding(t *testing.T) { // TODO: our config only support btcd wallet tls, not btcd directly tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, hex.EncodeToString(tm.MinerNode.RPCConfig().Certificates)), - &chaincfg.SimNetParams, + btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + &chaincfg.RegressionNetParams, &emptyHintCache, ) require.NoError(t, err) @@ -239,10 +165,7 @@ func TestSlasher_SlashingUnbonding(t *testing.T) { commonCfg := config.DefaultCommonConfig() bstCfg := config.DefaultBTCStakingTrackerConfig() bstCfg.CheckDelegationsInterval = 1 * time.Second - logger, err := config.NewRootLogger("auto", "debug") - require.NoError(t, err) - - metrics := metrics.NewBTCStakingTrackerMetrics() + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() bsTracker := bst.NewBTCSTakingTracker( tm.BTCClient, @@ -250,8 +173,8 @@ func TestSlasher_SlashingUnbonding(t *testing.T) { tm.BabylonClient, &bstCfg, &commonCfg, - logger, - metrics, + zap.NewNop(), + stakingTrackerMetrics, ) go bsTracker.Start() defer bsTracker.Stop() @@ -283,8 +206,14 @@ func TestSlasher_SlashingUnbonding(t *testing.T) { _, err := tm.BTCClient.GetRawTransaction(unbondingSlashingMsgTxHash) return err == nil }, eventuallyWaitTimeOut, eventuallyPollTime) + // mine a block that includes slashing tx - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingSlashingMsgTxHash})) + 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) @@ -299,32 +228,10 @@ func TestSlasher_Bootstrapping(t *testing.T) { // segwit is activated at height 300. It's needed by staking/slashing tx numMatureOutputs := uint32(300) - submittedTxs := []*chainhash.Hash{} - blockEventChan := make(chan *types.BlockEvent, 1000) - - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - log.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - log.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTxs = append(submittedTxs, hash) - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) - err = tm.MinerNode.Client.NotifyBlocks() - require.NoError(t, err) + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) // start WebSocket connection with Babylon for subscriber services - err = tm.BabylonClient.Start() + err := tm.BabylonClient.Start() require.NoError(t, err) // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) @@ -341,8 +248,8 @@ func TestSlasher_Bootstrapping(t *testing.T) { // TODO: our config only support btcd wallet tls, not btcd directly tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, hex.EncodeToString(tm.MinerNode.RPCConfig().Certificates)), - &chaincfg.SimNetParams, + btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + &chaincfg.RegressionNetParams, &emptyHintCache, ) require.NoError(t, err) @@ -353,10 +260,7 @@ func TestSlasher_Bootstrapping(t *testing.T) { commonCfg := config.DefaultCommonConfig() bstCfg := config.DefaultBTCStakingTrackerConfig() bstCfg.CheckDelegationsInterval = 1 * time.Second - logger, err := config.NewRootLogger("auto", "debug") - require.NoError(t, err) - - metrics := metrics.NewBTCStakingTrackerMetrics() + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() bsTracker := bst.NewBTCSTakingTracker( tm.BTCClient, @@ -364,8 +268,8 @@ func TestSlasher_Bootstrapping(t *testing.T) { tm.BabylonClient, &bstCfg, &commonCfg, - logger, - metrics, + zap.NewNop(), + stakingTrackerMetrics, ) // bootstrap BTC staking tracker @@ -377,15 +281,13 @@ func TestSlasher_Bootstrapping(t *testing.T) { require.NoError(t, err) slashingMsgTxHash1 := slashingMsgTx.TxHash() slashingMsgTxHash := &slashingMsgTxHash1 - require.Eventually(t, func() bool { - _, err := tm.BTCClient.GetRawTransaction(slashingMsgTxHash) - t.Logf("err of getting slashingMsgTxHash: %v", err) - return err == nil - }, eventuallyWaitTimeOut, eventuallyPollTime) + // mine a block that includes slashing tx - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{slashingMsgTxHash})) - // ensure 2 txs will eventually be received (staking tx and slashing tx) require.Eventually(t, func() bool { - return len(submittedTxs) == 2 + 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 index b4c1383a..55284a8f 100644 --- a/e2etest/submitter_e2e_test.go +++ b/e2etest/submitter_e2e_test.go @@ -13,7 +13,6 @@ import ( checkpointingtypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/rpcclient" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -24,22 +23,9 @@ import ( func TestSubmitterSubmission(t *testing.T) { r := rand.New(rand.NewSource(time.Now().Unix())) - numMatureOutputs := uint32(5) - - var submittedTransactions []*chainhash.Hash - - // We are setting handler for transaction hitting the mempool, to be sure we will - // pass transaction to the miner, in the same order as they were submitted by submitter - handlers := &rpcclient.NotificationHandlers{ - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTransactions = append(submittedTransactions, hash) - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, nil) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) randomCheckpoint := datagen.GenRandomRawCheckpointWithMeta(r) @@ -66,11 +52,12 @@ func TestSubmitterSubmission(t *testing.T) { }, nil).AnyTimes() tm.Config.Submitter.PollingIntervalSeconds = 2 + // create submitter vigilantSubmitter, _ := submitter.New( &tm.Config.Submitter, logger, - tm.BTCWalletClient, + tm.BTCClient, mockBabylonClient, subAddr, tm.Config.Common.RetrySleepTime, @@ -85,40 +72,32 @@ func TestSubmitterSubmission(t *testing.T) { vigilantSubmitter.WaitForShutdown() }() - // wait for our 2 op_returns with epoch 1 checkpoint to hit the mempool and then - // retrieve them from there - // - // TODO: to assert that those are really transactions send by submitter, we would - // need to expose sentCheckpointInfo from submitter + // 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(submittedTransactions) == 2 + return len(tm.RetrieveTransactionFromMempool(t, mempoolTxs)) == 2 }, eventuallyWaitTimeOut, eventuallyPollTime) - sendTransactions := tm.RetrieveTransactionFromMempool(t, submittedTransactions) // mine a block with those transactions - blockWithOpReturnTranssactions := tm.MineBlockWithTxs(t, sendTransactions) + blockWithOpReturnTransactions := tm.mineBlock(t) // block should have 3 transactions, 2 from submitter and 1 coinbase - require.Equal(t, len(blockWithOpReturnTranssactions.Transactions), 3) + require.Equal(t, len(blockWithOpReturnTransactions.Transactions), 3) } func TestSubmitterSubmissionReplace(t *testing.T) { r := rand.New(rand.NewSource(time.Now().Unix())) - numMatureOutputs := uint32(5) - - var submittedTransactions []*chainhash.Hash - - // We are setting handler for transaction hitting the mempool, to be sure we will - // pass transaction to the miner, in the same order as they were submitted by submitter - handlers := &rpcclient.NotificationHandlers{ - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTransactions = append(submittedTransactions, hash) - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, nil) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) + numMatureOutputs := uint32(300) + + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) randomCheckpoint := datagen.GenRandomRawCheckpointWithMeta(r) @@ -151,7 +130,7 @@ func TestSubmitterSubmissionReplace(t *testing.T) { vigilantSubmitter, _ := submitter.New( &tm.Config.Submitter, logger, - tm.BTCWalletClient, + tm.BTCClient, mockBabylonClient, subAddr, tm.Config.Common.RetrySleepTime, @@ -168,27 +147,27 @@ func TestSubmitterSubmissionReplace(t *testing.T) { // wait for our 2 op_returns with epoch 1 checkpoint to hit the mempool and then // retrieve them from there - // - // TODO: to assert that those are really transactions send by submitter, we would - // need to expose sentCheckpointInfo from submitter - require.Eventually(t, func() bool { - return len(submittedTransactions) == 2 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - sendTransactions := tm.RetrieveTransactionFromMempool(t, submittedTransactions) - - // at this point our submitter already sent 2 checkpoint transactions which landed in mempool. - // Zero out submittedTransactions, and wait for a new tx2 to be submitted and accepted - // it should be replacements for the previous one. - submittedTransactions = []*chainhash.Hash{} + txsMap := make(map[string]struct{}) + var sendTransactions []*btcutil.Tx + var mempoolTxs []*chainhash.Hash require.Eventually(t, func() bool { - // we only replace tx2 of the checkpoint, thus waiting for 1 tx to arrive - return len(submittedTransactions) == 1 - }, eventuallyWaitTimeOut, eventuallyPollTime) - - transactionReplacement := tm.RetrieveTransactionFromMempool(t, submittedTransactions) - resendTx2 := transactionReplacement[0] + 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 @@ -196,11 +175,10 @@ func TestSubmitterSubmissionReplace(t *testing.T) { // 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].SignatureScript, resendTx2.MsgTx().TxIn[0].SignatureScript) + 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 - sendTransactions[1] = resendTx2 - blockWithOpReturnTransactions := tm.MineBlockWithTxs(t, sendTransactions) + blockWithOpReturnTransactions := tm.mineBlock(t) // block should have 2 transactions, 1 from submitter and 1 coinbase require.Equal(t, len(blockWithOpReturnTransactions.Transactions), 3) } diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 0395cc02..c3f21be2 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -1,11 +1,10 @@ package e2etest import ( + "bytes" "context" - "encoding/binary" "encoding/hex" - "os" - "path/filepath" + "go.uber.org/zap" "testing" "time" @@ -19,164 +18,58 @@ import ( "github.com/babylonlabs-io/vigilante/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/integration/rpctest" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" "github.com/stretchr/testify/require" ) -// bticoin params used for testing var ( - netParams = &chaincfg.SimNetParams submitterAddrStr = "bbn1eppc73j56382wjn6nnq3quu5eye4pmm087xfdh" //nolint:unused babylonTag = []byte{1, 2, 3, 4} //nolint:unused babylonTagHex = hex.EncodeToString(babylonTag) //nolint:unused - // copy of the seed from btcd/integration/rpctest memWallet, this way we can - // import the same wallet in the btcd wallet - hdSeed = [chainhash.HashSize]byte{ - 0x79, 0xa6, 0x1a, 0xdb, 0xc6, 0xe5, 0xa2, 0xe1, - 0x39, 0xd2, 0x71, 0x3a, 0x54, 0x6e, 0xc7, 0xc8, - 0x75, 0x63, 0x2e, 0x75, 0xf1, 0xdf, 0x9c, 0x3f, - 0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - } - - // current number of active test nodes. This is necessary to replicate btcd rpctest.Harness - // methods of generating keys i.e with each started btcd node we increment this number - // by 1, and then use hdSeed || numTestInstances as the seed for generating keys - numTestInstances = 0 - - existingWalletFile = "wallet.db" - exisitngWalletPass = "pass" - walletTimeout = 86400 - eventuallyWaitTimeOut = 40 * time.Second eventuallyPollTime = 1 * time.Second + regtestParams = &chaincfg.RegressionNetParams ) -// keyToAddr maps the passed private to corresponding p2pkh address. -func keyToAddr(key *btcec.PrivateKey, net *chaincfg.Params) (btcutil.Address, error) { - serializedKey := key.PubKey().SerializeCompressed() - pubKeyAddr, err := btcutil.NewAddressPubKey(serializedKey, net) - if err != nil { - return nil, err - } - return pubKeyAddr.AddressPubKeyHash(), nil -} - func defaultVigilanteConfig() *config.Config { defaultConfig := config.DefaultConfig() // Config setting necessary to connect btcd daemon - defaultConfig.BTC.NetParams = "simnet" - defaultConfig.BTC.Endpoint = "127.0.0.1:18556" + defaultConfig.BTC.NetParams = regtestParams.Name + defaultConfig.BTC.Endpoint = "127.0.0.1:18443" // Config setting necessary to connect btcwallet daemon - defaultConfig.BTC.BtcBackend = "btcd" + defaultConfig.BTC.BtcBackend = types.Bitcoind defaultConfig.BTC.WalletEndpoint = "127.0.0.1:18554" defaultConfig.BTC.WalletPassword = "pass" defaultConfig.BTC.Username = "user" defaultConfig.BTC.Password = "pass" defaultConfig.BTC.DisableClientTLS = true - return defaultConfig -} - -func GetSpendingKeyAndAddress(id uint32) (*btcec.PrivateKey, btcutil.Address, error) { - var harnessHDSeed [chainhash.HashSize + 4]byte - copy(harnessHDSeed[:], hdSeed[:]) - // id used for our test wallet is always 0 - binary.BigEndian.PutUint32(harnessHDSeed[:chainhash.HashSize], id) - - hdRoot, err := hdkeychain.NewMaster(harnessHDSeed[:], netParams) + defaultConfig.BTC.ZmqSeqEndpoint = config.DefaultZmqSeqEndpoint - if err != nil { - return nil, nil, err - } - - // The first child key from the hd root is reserved as the coinbase - // generation address. - coinbaseChild, err := hdRoot.Derive(0) - if err != nil { - return nil, nil, err - } - - coinbaseKey, err := coinbaseChild.ECPrivKey() - - if err != nil { - return nil, nil, err - } - - coinbaseAddr, err := keyToAddr(coinbaseKey, netParams) - if err != nil { - return nil, nil, err - } - - return coinbaseKey, coinbaseAddr, nil + return defaultConfig } type TestManager struct { - MinerNode *rpctest.Harness - BtcWalletHandler *WalletHandler - BabylonHandler *BabylonNodeHandler - BabylonClient *bbnclient.Client - BTCClient *btcclient.Client - BTCWalletClient *btcclient.Client - Config *config.Config -} - -func initBTCWalletClient( - t *testing.T, - cfg *config.Config, - walletPrivKey *btcec.PrivateKey, - outputsToWaitFor int) *btcclient.Client { - - var client *btcclient.Client - - require.Eventually(t, func() bool { - btcWallet, err := btcclient.NewWallet(&cfg.BTC, logger) - if err != nil { - return false - } - - client = btcWallet - return true - - }, eventuallyWaitTimeOut, eventuallyPollTime) - - // lets wait until chain rpc becomes available - // poll time is increase here to avoid spamming the btcwallet rpc server - require.Eventually(t, func() bool { - if _, _, err := client.GetBestBlock(); err != nil { - return false - } - - return true - }, eventuallyWaitTimeOut, 1*time.Second) - - err := ImportWalletSpendingKey(t, client, walletPrivKey) - require.NoError(t, err) - - waitForNOutputs(t, client, outputsToWaitFor) - - return client + TestRpcClient *rpcclient.Client + BitcoindHandler *BitcoindTestHandler + BabylonHandler *BabylonNodeHandler + BabylonClient *bbnclient.Client + BTCClient *btcclient.Client + Config *config.Config + WalletPrivKey *btcec.PrivateKey } -func initBTCClientWithSubscriber(t *testing.T, cfg *config.Config, rpcClient *rpcclient.Client, connCfg *rpcclient.ConnConfig, blockEventChan chan *types.BlockEvent) *btcclient.Client { - btcCfg := &config.BTCConfig{ - NetParams: cfg.BTC.NetParams, - Username: connCfg.User, - Password: connCfg.Pass, - Endpoint: connCfg.Host, - DisableClientTLS: connCfg.DisableTLS, - } - client, err := btcclient.NewTestClientWithWsSubscriber(rpcClient, btcCfg, cfg.Common.RetrySleepTime, cfg.Common.MaxRetrySleepTime, blockEventChan) +func initBTCClientWithSubscriber(t *testing.T, cfg *config.Config) *btcclient.Client { + client, err := btcclient.NewWithBlockSubscriber(&cfg.BTC, cfg.Common.RetrySleepTime, cfg.Common.MaxRetrySleepTime, zap.NewNop()) require.NoError(t, err) - // lets wait until chain rpc becomes available + // 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.GetBestBlock(); err != nil { + if _, err := client.GetBlockCount(); err != nil { log.Errorf("failed to get best block: %v", err) return false } @@ -188,82 +81,51 @@ func initBTCClientWithSubscriber(t *testing.T, cfg *config.Config, rpcClient *rp } // StartManager creates a test manager -// NOTE: if handlers.OnFilteredBlockConnected, handlers.OnFilteredBlockDisconnected -// and blockEventChan are all not nil, then the test manager will create a BTC -// client with a WebSocket subscriber -func StartManager( - t *testing.T, - numMatureOutputsInWallet uint32, - numbersOfOutputsToWaitForDuringInit int, - handlers *rpcclient.NotificationHandlers, - blockEventChan chan *types.BlockEvent) *TestManager { - args := []string{ - "--rejectnonstd", - "--txindex", - "--trickleinterval=100ms", - "--debuglevel=debug", - "--nowinservice", - // The miner will get banned and disconnected from the node if - // its requested data are not found. We add a nobanning flag to - // make sure they stay connected if it happens. - "--nobanning", - // Don't disconnect if a reply takes too long. - "--nostalldetect", - } +// NOTE: uses btc client with zmq +func StartManager(t *testing.T, numMatureOutputsInWallet uint32) *TestManager { + btcHandler := NewBitcoindHandler(t) + btcHandler.Start() + passphrase := "pass" + _ = btcHandler.CreateWallet("default", passphrase) + blocksResponse := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) - miner, err := rpctest.New(netParams, handlers, args, "") - require.NoError(t, err) + cfg := defaultVigilanteConfig() - privkey, _, err := GetSpendingKeyAndAddress(uint32(numTestInstances)) + 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) - if err := miner.SetUp(true, numMatureOutputsInWallet); err != nil { - t.Fatalf("unable to set up mining node: %v", err) - } + btcClient := initBTCClientWithSubscriber(t, cfg) - minerNodeRpcConfig := miner.RPCConfig() - certFile := minerNodeRpcConfig.Certificates - - currentDir, err := os.Getwd() + var buff bytes.Buffer + err = regtestParams.GenesisBlock.Header.Serialize(&buff) require.NoError(t, err) - walletPath := filepath.Join(currentDir, existingWalletFile) + baseHeaderHex := hex.EncodeToString(buff.Bytes()) - // start Bitcoin wallet - wh, err := NewWalletHandler(certFile, walletPath, minerNodeRpcConfig.Host) - require.NoError(t, err) - err = wh.Start() + minerAddressDecoded, err := btcutil.DecodeAddress(blocksResponse.Address, regtestParams) require.NoError(t, err) - // Wait for wallet to re-index the outputs - time.Sleep(5 * time.Second) - - cfg := defaultVigilanteConfig() - cfg.BTC.Endpoint = minerNodeRpcConfig.Host - - var btcClient *btcclient.Client - if handlers.OnFilteredBlockConnected != nil && handlers.OnFilteredBlockDisconnected != nil { - // BTC client with subscriber - btcClient = initBTCClientWithSubscriber(t, cfg, miner.Client, &minerNodeRpcConfig, blockEventChan) - } - // we always want BTC wallet client for sending txs - btcWalletClient := initBTCWalletClient( - t, - cfg, - privkey, - numbersOfOutputsToWaitForDuringInit, - ) - // start Babylon node - bh, err := NewBabylonNodeHandler() + bh, err := NewBabylonNodeHandler(baseHeaderHex, minerAddressDecoded.EncodeAddress()) require.NoError(t, err) err = bh.Start() require.NoError(t, err) + // create Babylon client cfg.Babylon.KeyDirectory = bh.GetNodeDataDir() - cfg.Babylon.Key = "test-spending-key" + cfg.Babylon.Key = "test-spending-key" // keyring to bbn node cfg.Babylon.GasAdjustment = 3.0 + 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() @@ -274,82 +136,60 @@ func StartManager( return true }, eventuallyWaitTimeOut, eventuallyPollTime) - numTestInstances++ + err = testRpcClient.WalletPassphrase(passphrase, 600) + require.NoError(t, err) + + walletPrivKey, err := testRpcClient.DumpPrivKey(minerAddressDecoded) + require.NoError(t, err) return &TestManager{ - MinerNode: miner, - BtcWalletHandler: wh, - BabylonHandler: bh, - BabylonClient: babylonClient, - BTCClient: btcClient, - BTCWalletClient: btcWalletClient, - Config: cfg, + TestRpcClient: testRpcClient, + BabylonHandler: bh, + BabylonClient: babylonClient, + BitcoindHandler: btcHandler, + BTCClient: btcClient, + Config: cfg, + WalletPrivKey: walletPrivKey.PrivKey, } } func (tm *TestManager) Stop(t *testing.T) { - err := tm.BtcWalletHandler.Stop() - require.NoError(t, err) - err = tm.MinerNode.TearDown() + err := tm.BabylonHandler.Stop() require.NoError(t, err) + if tm.BabylonClient.IsRunning() { - err = tm.BabylonClient.Stop() + err := tm.BabylonClient.Stop() require.NoError(t, err) } - err = tm.BabylonHandler.Stop() - require.NoError(t, err) } -func ImportWalletSpendingKey( - t *testing.T, - walletClient *btcclient.Client, - privKey *btcec.PrivateKey) error { +// mineBlock mines a single block +func (tm *TestManager) mineBlock(t *testing.T) *wire.MsgBlock { + resp := tm.BitcoindHandler.GenerateBlocks(1) - wifKey, err := btcutil.NewWIF(privKey, netParams, true) + hash, err := chainhash.NewHashFromStr(resp.Blocks[0]) require.NoError(t, err) - err = walletClient.WalletPassphrase(exisitngWalletPass, int64(walletTimeout)) - - if err != nil { - return err - } - - err = walletClient.ImportPrivKey(wifKey) - - if err != nil { - return err - } - - return nil -} - -// MineBlocksWithTxes mines a single block to include the specifies -// transactions only. -func (tm *TestManager) MineBlockWithTxs(t *testing.T, txs []*btcutil.Tx) *wire.MsgBlock { - var emptyTime time.Time - - // Generate a block. - b, err := tm.MinerNode.GenerateAndSubmitBlock(txs, -1, emptyTime) - require.NoError(t, err, "unable to mine block") - - block, err := tm.MinerNode.Client.GetBlock(b.Hash()) - require.NoError(t, err, "unable to get block") + header, err := tm.TestRpcClient.GetBlock(hash) + require.NoError(t, err) - return block + 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 txes []*btcutil.Tx + var txs []*btcutil.Tx for _, txHash := range hashes { tx, err := tm.BTCClient.GetRawTransaction(txHash) require.NoError(t, err) - txes = append(txes, tx) + txs = append(txs, tx) } - return txes + + return txs } func (tm *TestManager) InsertBTCHeadersToBabylon(headers []*wire.BlockHeader) (*pv.RelayerTxResponse, error) { @@ -368,18 +208,18 @@ func (tm *TestManager) InsertBTCHeadersToBabylon(headers []*wire.BlockHeader) (* } func (tm *TestManager) CatchUpBTCLightClient(t *testing.T) { - _, btcHeight, err := tm.MinerNode.Client.GetBestBlock() + btcHeight, err := tm.TestRpcClient.GetBlockCount() require.NoError(t, err) tipResp, err := tm.BabylonClient.BTCHeaderChainTip() require.NoError(t, err) btclcHeight := tipResp.Header.Height - headers := []*wire.BlockHeader{} + var headers []*wire.BlockHeader for i := int(btclcHeight + 1); i <= int(btcHeight); i++ { - hash, err := tm.MinerNode.Client.GetBlockHash(int64(i)) + hash, err := tm.TestRpcClient.GetBlockHash(int64(i)) require.NoError(t, err) - header, err := tm.MinerNode.Client.GetBlockHeader(hash) + header, err := tm.TestRpcClient.GetBlockHeader(hash) require.NoError(t, err) headers = append(headers, header) } @@ -387,15 +227,3 @@ func (tm *TestManager) CatchUpBTCLightClient(t *testing.T) { _, err = tm.InsertBTCHeadersToBabylon(headers) require.NoError(t, err) } - -func waitForNOutputs(t *testing.T, walletClient *btcclient.Client, n int) { - require.Eventually(t, func() bool { - outputs, err := walletClient.ListUnspent() - - if err != nil { - return false - } - - return len(outputs) >= n - }, eventuallyWaitTimeOut, eventuallyPollTime) -} diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index a780aa77..579a5eea 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -93,23 +93,20 @@ func (tm *TestManager) CreateBTCDelegation( require.NoError(t, err) stakingTimeBlocks := uint16(math.MaxUint16) // get top UTXO - topUnspentResult, _, err := tm.BTCWalletClient.GetHighUTXOAndSum() + topUnspentResult, _, err := tm.BTCClient.GetHighUTXOAndSum() require.NoError(t, err) - topUTXO, err := types.NewUTXO(topUnspentResult, netParams) + topUTXO, err := types.NewUTXO(topUnspentResult, regtestParams) require.NoError(t, err) // staking value stakingValue := int64(topUTXO.Amount) / 3 - // dump SK - wif, err := tm.BTCWalletClient.DumpPrivKey(topUTXO.Addr) - require.NoError(t, err) // generate legitimate BTC del stakingSlashingInfo := datagen.GenBTCStakingSlashingInfoWithOutPoint( r, t, - netParams, + regtestParams, topUTXO.GetOutPoint(), - wif.PrivKey, + tm.WalletPrivKey, []*btcec.PublicKey{fpPK}, covenantBtcPks, bsParams.Params.CovenantQuorum, @@ -121,7 +118,7 @@ func (tm *TestManager) CreateBTCDelegation( ) // 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.BTCWalletClient.SignRawTransaction(stakingSlashingInfo.StakingTx) + stakingMsgTx, signed, err := tm.BTCClient.SignRawTransactionWithWallet(stakingSlashingInfo.StakingTx) require.NoError(t, err) require.True(t, signed) // overwrite staking tx @@ -140,12 +137,16 @@ func (tm *TestManager) CreateBTCDelegation( require.NoError(t, err) // send staking tx to Bitcoin node's mempool - _, err = tm.BTCWalletClient.SendRawTransaction(stakingMsgTx, true) + _, err = tm.BTCClient.SendRawTransaction(stakingMsgTx, true) require.NoError(t, err) - // mine a block with this tx, and insert it to Bitcoin / Babylon - mBlock := tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{stakingMsgTxHash})) + 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) @@ -159,14 +160,15 @@ func (tm *TestManager) CreateBTCDelegation( require.NoError(t, err) btccParams := btccParamsResp.Params for i := 0; i < int(btccParams.BtcConfirmationDepth); i++ { - tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{})) + //tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{})) + tm.mineBlock(t) } stakingOutIdx, err := outIdx(stakingSlashingInfo.StakingTx, stakingSlashingInfo.StakingInfo.StakingOutput) require.NoError(t, err) // create PoP - pop, err := bstypes.NewPoPBTC(addr, wif.PrivKey) + pop, err := bstypes.NewPoPBTC(addr, tm.WalletPrivKey) require.NoError(t, err) slashingSpendPath, err := stakingSlashingInfo.StakingInfo.SlashingPathSpendInfo() require.NoError(t, err) @@ -176,7 +178,7 @@ func (tm *TestManager) CreateBTCDelegation( stakingMsgTx, stakingOutIdx, slashingSpendPath.GetPkScriptPath(), - wif.PrivKey, + tm.WalletPrivKey, ) require.NoError(t, err) @@ -186,8 +188,8 @@ func (tm *TestManager) CreateBTCDelegation( unbondingSlashingInfo := datagen.GenBTCUnbondingSlashingInfo( r, t, - netParams, - wif.PrivKey, + regtestParams, + tm.WalletPrivKey, []*btcec.PublicKey{fpPK}, covenantBtcPks, bsParams.Params.CovenantQuorum, @@ -208,7 +210,7 @@ func (tm *TestManager) CreateBTCDelegation( unbondingSlashingInfo.UnbondingTx, 0, // Only one output in the unbonding tx unbondingSlashingPathSpendInfo.GetPkScriptPath(), - wif.PrivKey, + tm.WalletPrivKey, ) require.NoError(t, err) @@ -219,7 +221,7 @@ func (tm *TestManager) CreateBTCDelegation( msgBTCDel := &bstypes.MsgCreateBTCDelegation{ StakerAddr: signerAddr, Pop: pop, - BtcPk: bbn.NewBIP340PubKeyFromBTCPK(wif.PrivKey.PubKey()), + BtcPk: bbn.NewBIP340PubKeyFromBTCPK(tm.WalletPrivKey.PubKey()), FpBtcPkList: []bbn.BIP340PubKey{*bbn.NewBIP340PubKeyFromBTCPK(fpPK)}, StakingTime: uint32(stakingTimeBlocks), StakingValue: stakingValue, @@ -288,7 +290,7 @@ func (tm *TestManager) CreateBTCDelegation( require.NoError(t, err) t.Logf("submitted covenant signature") - return stakingSlashingInfo, unbondingSlashingInfo, wif.PrivKey + return stakingSlashingInfo, unbondingSlashingInfo, tm.WalletPrivKey } func (tm *TestManager) Undelegate( @@ -334,16 +336,22 @@ func (tm *TestManager) Undelegate( unbondingSlashingInfo.UnbondingTx.TxIn[0].Witness = witness // send unbonding tx to Bitcoin node's mempool - unbondingTxHash, err := tm.BTCWalletClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) + 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 - mBlock := tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{unbondingTxHash})) + 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)) + // wait until unbonding tx is on Bitcoin require.Eventually(t, func() bool { resp, err := tm.BTCClient.GetRawTransactionVerbose(unbondingTxHash) diff --git a/e2etest/unbondingwatcher_e2e_test.go b/e2etest/unbondingwatcher_e2e_test.go index 53ef2fcf..fc60c51f 100644 --- a/e2etest/unbondingwatcher_e2e_test.go +++ b/e2etest/unbondingwatcher_e2e_test.go @@ -4,7 +4,7 @@ package e2etest import ( - "encoding/hex" + "go.uber.org/zap" "testing" "time" @@ -13,13 +13,9 @@ import ( bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" "github.com/babylonlabs-io/vigilante/config" "github.com/babylonlabs-io/vigilante/metrics" - "github.com/babylonlabs-io/vigilante/types" "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/stretchr/testify/require" ) @@ -27,28 +23,7 @@ func TestUnbondingWatcher(t *testing.T) { // segwit is activated at height 300. It's needed by staking/slashing tx numMatureOutputs := uint32(300) - submittedTxs := []*chainhash.Hash{} - blockEventChan := make(chan *types.BlockEvent, 1000) - handlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - log.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - log.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - OnTxAccepted: func(hash *chainhash.Hash, amount btcutil.Amount) { - submittedTxs = append(submittedTxs, hash) - }, - } - - tm := StartManager(t, numMatureOutputs, 2, handlers, blockEventChan) - // this is necessary to receive notifications about new transactions entering mempool - err := tm.MinerNode.Client.NotifyNewTransactions(false) - require.NoError(t, err) - err = tm.MinerNode.Client.NotifyBlocks() - require.NoError(t, err) + tm := StartManager(t, numMatureOutputs) defer tm.Stop(t) // Insert all existing BTC headers to babylon node tm.CatchUpBTCLightClient(t) @@ -58,8 +33,8 @@ func TestUnbondingWatcher(t *testing.T) { // TODO: our config only support btcd wallet tls, not btcd directly tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, hex.EncodeToString(tm.MinerNode.RPCConfig().Certificates)), - &chaincfg.SimNetParams, + btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + &chaincfg.RegressionNetParams, &emptyHintCache, ) require.NoError(t, err) @@ -70,10 +45,7 @@ func TestUnbondingWatcher(t *testing.T) { commonCfg := config.DefaultCommonConfig() bstCfg := config.DefaultBTCStakingTrackerConfig() bstCfg.CheckDelegationsInterval = 1 * time.Second - logger, err := config.NewRootLogger("auto", "debug") - require.NoError(t, err) - - metrics := metrics.NewBTCStakingTrackerMetrics() + stakingTrackerMetrics := metrics.NewBTCStakingTrackerMetrics() bsTracker := bst.NewBTCSTakingTracker( tm.BTCClient, @@ -81,24 +53,23 @@ func TestUnbondingWatcher(t *testing.T) { tm.BabylonClient, &bstCfg, &commonCfg, - logger, - metrics, + zap.NewNop(), + stakingTrackerMetrics, ) bsTracker.Start() defer bsTracker.Stop() // set up a finality provider _, fpSK := tm.CreateFinalityProvider(t) - logger.Info("created finality provider") // set up a BTC delegation stakingSlashingInfo, unbondingSlashingInfo, delSK := tm.CreateBTCDelegation(t, fpSK) - logger.Info("created BTC delegation") // 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, @@ -107,22 +78,30 @@ func TestUnbondingWatcher(t *testing.T) { 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.BTCWalletClient.SendRawTransaction(unbondingSlashingInfo.UnbondingTx, true) + _, 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()) - mBlock := tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{&unbondingTxHash})) - require.Equal(t, 2, len(mBlock.Transactions)) + 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)) require.Eventually(t, func() bool { resp, err := tm.BabylonClient.BTCDelegation(stakingSlashingInfo.StakingTx.TxHash().String()) diff --git a/go.mod b/go.mod index ee3c7c95..fc04e971 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/jsternberg/zap-logfmt v1.3.0 github.com/lightningnetwork/lnd v0.16.4-beta.rc1 + github.com/ory/dockertest/v3 v3.9.1 github.com/pebbe/zmq4 v1.2.9 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.0 @@ -63,10 +64,13 @@ require ( cosmossdk.io/x/upgrade v0.1.1 // indirect filippo.io/edwards25519 v1.0.0 // 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.51.0 // indirect github.com/CosmWasm/wasmvm/v2 v2.0.1 // 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 @@ -93,6 +97,7 @@ require ( github.com/cometbft/cometbft-db v0.9.1 // 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 @@ -119,7 +124,10 @@ require ( github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/distribution/reference v0.5.0 // indirect + github.com/docker/cli v23.0.1+incompatible // indirect github.com/docker/docker v23.0.8+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 @@ -148,6 +156,7 @@ require ( 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.0 // indirect @@ -172,6 +181,7 @@ require ( github.com/holiman/uint256 v1.2.4 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -220,6 +230,7 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mtibben/percent v0.2.1 // indirect @@ -227,6 +238,8 @@ require ( 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.5 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect github.com/pierrec/lz4/v4 v4.1.8 // indirect @@ -259,6 +272,9 @@ require ( github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ulikunitz/xz v0.5.11 // 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/zondax/hid v0.9.2 // indirect diff --git a/go.sum b/go.sum index 32b26993..27d54b94 100644 --- a/go.sum +++ b/go.sum @@ -361,6 +361,7 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= 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.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -371,6 +372,7 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= 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= @@ -412,6 +414,7 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -422,6 +425,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -469,6 +473,9 @@ github.com/creachadair/tomledit v0.0.24 h1:5Xjr25R2esu1rKCbQEmjZYlrhFkDspoAbAKb6 github.com/creachadair/tomledit v0.0.24/go.mod h1:9qHbShRWQzSCcn617cMzg4eab1vbLCOjOshAWSzWr8U= 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/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 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= @@ -498,10 +505,13 @@ 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 v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= +github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v23.0.8+incompatible h1:z4ZCIwfqHgOEwhxmAWugSL1PFtPQmLP60EVhJYJPaX8= github.com/docker/docker v23.0.8+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.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= @@ -549,6 +559,7 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -601,6 +612,8 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn 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/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -614,6 +627,7 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -724,6 +738,8 @@ github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHa 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= @@ -837,6 +853,8 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 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= @@ -950,6 +968,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1059,6 +1078,9 @@ 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/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/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= @@ -1066,6 +1088,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1118,6 +1141,8 @@ github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7X github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -1128,6 +1153,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.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= +github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= 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= @@ -1228,6 +1255,7 @@ github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71e github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -1239,6 +1267,7 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -1299,6 +1328,7 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -1329,8 +1359,17 @@ github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vulpine-io/io-test v1.0.0 h1:Ot8vMh+ssm1VWDAwJ3U4C5qG9aRnr5YfQFZPNZBAUGI= github.com/vulpine-io/io-test v1.0.0/go.mod h1:X1I+p5GCxVX9m4nFd1HBtr2bVX9v1ZE6x8w+Obt36AU= +github.com/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= @@ -1525,6 +1564,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= @@ -1609,6 +1649,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1619,6 +1660,7 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1650,6 +1692,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1668,9 +1711,11 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/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-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/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= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2067,6 +2112,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= diff --git a/tools/go.mod b/tools/go.mod index 577a04bf..09da8089 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -6,8 +6,6 @@ toolchain go1.21.4 require ( github.com/babylonlabs-io/babylon v0.9.1 - github.com/btcsuite/btcd v0.24.2 - github.com/btcsuite/btcwallet v0.16.9 ) require ( diff --git a/tools/tools.go b/tools/tools.go index 54f759ac..01550773 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -5,6 +5,4 @@ package vigilante import ( _ "github.com/babylonlabs-io/babylon/cmd/babylond" - _ "github.com/btcsuite/btcd" - _ "github.com/btcsuite/btcwallet" ) From b583081e030bd4902c1469bc00105578a9573770 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:34:09 +0200 Subject: [PATCH 03/12] fix(ci): fix publish action (#10) fixes publish action with `apt-get update` --- .github/workflows/publish.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8af43ef7..b49b58f6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,7 +15,10 @@ jobs: run-unit-tests: true run-integration-tests: true run-lint: true - install-dependencies-command: sudo apt-get install libzmq3-dev + install-dependencies-command: | + sudo apt-get update + sudo apt-get install -y libzmq3-dev + docker_pipeline: needs: ["lint_test"] From 2d50630e83f9cd94e11ce313190512b88543d1cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:37:08 +0200 Subject: [PATCH 04/12] chore(deps): bump github.com/hashicorp/go-getter from 1.7.3 to 1.7.5 in /tools (#12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.7.3 to 1.7.5.
Release notes

Sourced from github.com/hashicorp/go-getter's releases.

v1.7.5

What's Changed

New Contributors

Full Changelog: https://github.com/hashicorp/go-getter/compare/v1.7.4...v1.7.5

v1.7.4

What's Changed

Full Changelog: https://github.com/hashicorp/go-getter/compare/v1.7.3...v1.7.4

Commits
  • 5a63fd9 Merge pull request #497 from hashicorp/fix-git-update
  • 5b7ec5f fetch tags on update and fix tests
  • 9906874 recreate git config during update to prevent config alteration
  • 268c11c escape user provide string to git (#483)
  • 975961f Merge pull request #433 from adrian-bl/netrc-fix
  • 5ccb39a Make addAuthFromNetrc ignore ENOTDIR errors
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/hashicorp/go-getter&package-manager=go_modules&previous-version=1.7.3&new-version=1.7.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/babylonlabs-io/vigilante/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/go.mod | 26 +++----------------------- tools/go.sum | 46 ++-------------------------------------------- 2 files changed, 5 insertions(+), 67 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index 09da8089..ce29ae53 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -4,9 +4,7 @@ go 1.21 toolchain go1.21.4 -require ( - github.com/babylonlabs-io/babylon v0.9.1 -) +require github.com/babylonlabs-io/babylon v0.9.1 require ( cloud.google.com/go v0.112.0 // indirect @@ -44,19 +42,11 @@ require ( github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d // indirect + github.com/btcsuite/btcd v0.24.2 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect - github.com/btcsuite/btcd/btcutil/psbt v1.1.8 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect - github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 // indirect - github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 // indirect - github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 // indirect - github.com/btcsuite/btcwallet/walletdb v1.4.0 // indirect - github.com/btcsuite/btcwallet/wtxmgr v1.5.0 // indirect - github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect - github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect - github.com/btcsuite/winsvc v1.0.0 // indirect github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -88,7 +78,6 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/decred/dcrd/lru v1.0.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect @@ -130,7 +119,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.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.3 // indirect + github.com/hashicorp/go-getter v1.7.5 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.3 // indirect @@ -145,24 +134,15 @@ require ( github.com/iancoleman/strcase v0.3.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/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/jrick/logrotate v1.0.0 // indirect github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect - github.com/lightninglabs/neutrino v0.15.0 // indirect - github.com/lightninglabs/neutrino/cache v1.1.0 // indirect - github.com/lightningnetwork/lnd/clock v1.0.1 // indirect - github.com/lightningnetwork/lnd/queue v1.0.1 // indirect - github.com/lightningnetwork/lnd/ticker v1.0.0 // indirect - github.com/lightningnetwork/lnd/tlv v1.0.2 // indirect github.com/linxGnu/grocksdb v1.8.14 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 6763dd5f..b6ffb2fb 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -288,24 +288,17 @@ github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvP github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879/go.mod h1:osu7EoKiL36UThEgzYPqdRaxeo0NU8VoXqgcnwpey0g= -github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= -github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= -github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= -github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= -github.com/btcsuite/btcd/btcutil/psbt v1.1.8/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= @@ -313,29 +306,12 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.9 h1:hLAzEJvsiSn+r6j374G7ThnrYD/toa+Lv7l1Rm6+0oM= -github.com/btcsuite/btcwallet v0.16.9/go.mod h1:T3DjEAMZYIqQ28l+ixlB6DX4mFJXCX8Pzz+yACQcLsc= -github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 h1:etuLgGEojecsDOYTII8rYiGHjGyV5xTqsXi+ZQ715UU= -github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2/go.mod h1:Zpk/LOb2sKqwP2lmHjaZT9AdaKsHPSbNLm2Uql5IQ/0= -github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg= -github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.2/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 h1:PszOub7iXVYbtGybym5TGCp9Dv1h1iX4rIC3HICZGLg= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= -github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= -github.com/btcsuite/btcwallet/walletdb v1.4.0 h1:/C5JRF+dTuE2CNMCO/or5N8epsrhmSM4710uBQoYPTQ= -github.com/btcsuite/btcwallet/walletdb v1.4.0/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= -github.com/btcsuite/btcwallet/wtxmgr v1.5.0 h1:WO0KyN4l6H3JWnlFxfGR7r3gDnlGT7W2cL8vl6av4SU= -github.com/btcsuite/btcwallet/wtxmgr v1.5.0/go.mod h1:TQVDhFxseiGtZwEPvLgtfyxuNUDsIdaJdshvWzR0HJ4= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= @@ -453,7 +429,6 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPc github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= @@ -728,8 +703,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E= -github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -786,7 +761,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= @@ -801,7 +775,6 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -845,20 +818,6 @@ github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= -github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= -github.com/lightninglabs/neutrino v0.15.0 h1:yr3uz36fLAq8hyM0TRUVlef1TRNoWAqpmmNlVtKUDtI= -github.com/lightninglabs/neutrino v0.15.0/go.mod h1:pmjwElN/091TErtSE9Vd5W4hpxoG2/+xlb+HoPm9Gug= -github.com/lightninglabs/neutrino/cache v1.1.0 h1:szZIhVabiQIsGzJjhvo76sj05Au+zVotj2M34EquGME= -github.com/lightninglabs/neutrino/cache v1.1.0/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= -github.com/lightningnetwork/lnd/clock v1.0.1 h1:QQod8+m3KgqHdvVMV+2DRNNZS1GRFir8mHZYA+Z2hFo= -github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= -github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0= -github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms= -github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU= -github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0= -github.com/lightningnetwork/lnd/tlv v1.0.2 h1:LG7H3Uw/mHYGnEeHRPg+STavAH+UsFvuBflD0PzcYFQ= -github.com/lightningnetwork/lnd/tlv v1.0.2/go.mod h1:fICAfsqk1IOsC1J7G9IdsWX1EqWRMqEDCNxZJSKr9C4= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= @@ -1140,7 +1099,6 @@ github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWp github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= From e42be2f3c04c8de4abc6b8fbcae849e2f7e20850 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:55:01 +0200 Subject: [PATCH 05/12] chore(deps): bump github.com/hashicorp/go-getter from 1.7.3 to 1.7.5 (#13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.7.3 to 1.7.5.
Release notes

Sourced from github.com/hashicorp/go-getter's releases.

v1.7.5

What's Changed

New Contributors

Full Changelog: https://github.com/hashicorp/go-getter/compare/v1.7.4...v1.7.5

v1.7.4

What's Changed

Full Changelog: https://github.com/hashicorp/go-getter/compare/v1.7.3...v1.7.4

Commits
  • 5a63fd9 Merge pull request #497 from hashicorp/fix-git-update
  • 5b7ec5f fetch tags on update and fix tests
  • 9906874 recreate git config during update to prevent config alteration
  • 268c11c escape user provide string to git (#483)
  • 975961f Merge pull request #433 from adrian-bl/netrc-fix
  • 5ccb39a Make addAuthFromNetrc ignore ENOTDIR errors
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/hashicorp/go-getter&package-manager=go_modules&previous-version=1.7.3&new-version=1.7.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/babylonlabs-io/vigilante/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fc04e971..4db3aa96 100644 --- a/go.mod +++ b/go.mod @@ -167,7 +167,7 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.7.3 // indirect + github.com/hashicorp/go-getter v1.7.5 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.3 // indirect diff --git a/go.sum b/go.sum index 27d54b94..ffc3ea90 100644 --- a/go.sum +++ b/go.sum @@ -796,8 +796,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E= -github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= From e9a1e24e289ec5e779d9b7aa9332d915f60da770 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:59:58 +0200 Subject: [PATCH 06/12] chore(submitter): remove dumpPrivKey (#15) This pull request removes the usage of the dumpPrivKey RPC method from the submitter and end-to-end (E2E) tests. The dumpPrivKey method is specific to legacy wallets and is no longer supported in the latest versions of bitcoind. Eliminating its usage is a necessary step for upgrading to newer versions of bitcoind, as its deprecation would otherwise hinder the upgrade process. Updates the version of bitcoind to v27.0 References [issue](https://github.com/babylonchain/vigilante/issues/227). --- btcclient/client_wallet.go | 8 ++ btcclient/interface.go | 2 + e2etest/bitcoind_node_setup.go | 15 +- e2etest/container/config.go | 2 +- e2etest/container/container.go | 1 + e2etest/test_manager.go | 51 +++++-- e2etest/test_manager_btcstaking.go | 1 - submitter/relayer/relayer.go | 218 +++++++++++++---------------- submitter/relayer/utils.go | 92 +----------- testutil/mocks/btcclient.go | 31 ++++ types/ckpt_info.go | 1 - 11 files changed, 196 insertions(+), 226 deletions(-) diff --git a/btcclient/client_wallet.go b/btcclient/client_wallet.go index 1504eacc..fe52e740 100644 --- a/btcclient/client_wallet.go +++ b/btcclient/client_wallet.go @@ -137,3 +137,11 @@ func (c *Client) GetHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, error func CalculateTxFee(feeRateAmount btcutil.Amount, size uint64) (uint64, error) { return uint64(feeRateAmount.MulF64(float64(size) / 1024)), nil } + +func (c *Client) FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) { + return c.Client.FundRawTransaction(tx, opts, isWitness) +} + +func (c *Client) SignRawTransactionWithWallet(tx *wire.MsgTx) (*wire.MsgTx, bool, error) { + return c.Client.SignRawTransactionWithWallet(tx) +} diff --git a/btcclient/interface.go b/btcclient/interface.go index 12adb6a5..d3021412 100644 --- a/btcclient/interface.go +++ b/btcclient/interface.go @@ -39,4 +39,6 @@ type BTCWallet interface { WalletPassphrase(passphrase string, timeoutSecs int64) error DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) GetHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, error) + FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) + SignRawTransactionWithWallet(tx *wire.MsgTx) (*wire.MsgTx, bool, error) } diff --git a/e2etest/bitcoind_node_setup.go b/e2etest/bitcoind_node_setup.go index 52ffb4d4..e6686eaf 100644 --- a/e2etest/bitcoind_node_setup.go +++ b/e2etest/bitcoind_node_setup.go @@ -67,6 +67,7 @@ func (h *BitcoindTestHandler) Start() { }, startTimeout, 500*time.Millisecond, "bitcoind did not start") } +// 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 { @@ -78,6 +79,7 @@ func (h *BitcoindTestHandler) GetBlockCount() (int, error) { 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) @@ -89,11 +91,10 @@ func (h *BitcoindTestHandler) GenerateBlocks(count int) *GenerateBlockResponse { 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 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"}) + // 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 @@ -108,3 +109,9 @@ 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) +} diff --git a/e2etest/container/config.go b/e2etest/container/config.go index b1d14313..01b75746 100644 --- a/e2etest/container/config.go +++ b/e2etest/container/config.go @@ -10,7 +10,7 @@ type ImageConfig struct { //nolint:deadcode const ( dockerBitcoindRepository = "lncm/bitcoind" - dockerBitcoindVersionTag = "v24.0.1" + dockerBitcoindVersionTag = "v27.0" ) // NewImageConfig returns ImageConfig needed for running e2e test. diff --git a/e2etest/container/container.go b/e2etest/container/container.go index c95bd19b..a2bab703 100644 --- a/e2etest/container/container.go +++ b/e2etest/container/container.go @@ -158,6 +158,7 @@ func (m *Manager) RunBitcoindResource( "-rpcallowip=0.0.0.0/0", "-rpcbind=0.0.0.0", "-zmqpubsequence=tcp://0.0.0.0:28333", + "-fallbackfee=0.0002", }, }, noRestart, diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index c3f21be2..7a82624e 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "encoding/hex" + "encoding/json" + "fmt" "go.uber.org/zap" "testing" "time" @@ -87,7 +89,6 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32) *TestManager { btcHandler.Start() passphrase := "pass" _ = btcHandler.CreateWallet("default", passphrase) - blocksResponse := btcHandler.GenerateBlocks(int(numMatureOutputsInWallet)) cfg := defaultVigilanteConfig() @@ -102,6 +103,13 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32) *TestManager { }, 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 @@ -136,12 +144,6 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32) *TestManager { return true }, eventuallyWaitTimeOut, eventuallyPollTime) - err = testRpcClient.WalletPassphrase(passphrase, 600) - require.NoError(t, err) - - walletPrivKey, err := testRpcClient.DumpPrivKey(minerAddressDecoded) - require.NoError(t, err) - return &TestManager{ TestRpcClient: testRpcClient, BabylonHandler: bh, @@ -149,7 +151,7 @@ func StartManager(t *testing.T, numMatureOutputsInWallet uint32) *TestManager { BitcoindHandler: btcHandler, BTCClient: btcClient, Config: cfg, - WalletPrivKey: walletPrivKey.PrivKey, + WalletPrivKey: walletPrivKey, } } @@ -227,3 +229,36 @@ func (tm *TestManager) CatchUpBTCLightClient(t *testing.T) { _, 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 +} diff --git a/e2etest/test_manager_btcstaking.go b/e2etest/test_manager_btcstaking.go index 579a5eea..c8b1bcdc 100644 --- a/e2etest/test_manager_btcstaking.go +++ b/e2etest/test_manager_btcstaking.go @@ -160,7 +160,6 @@ func (tm *TestManager) CreateBTCDelegation( require.NoError(t, err) btccParams := btccParamsResp.Params for i := 0; i < int(btccParams.BtcConfirmationDepth); i++ { - //tm.MineBlockWithTxs(t, tm.RetrieveTransactionFromMempool(t, []*chainhash.Hash{})) tm.mineBlock(t) } diff --git a/submitter/relayer/relayer.go b/submitter/relayer/relayer.go index 615921d8..1e23ebb0 100644 --- a/submitter/relayer/relayer.go +++ b/submitter/relayer/relayer.go @@ -3,7 +3,9 @@ package relayer import ( "bytes" "encoding/hex" + "errors" "fmt" + "github.com/btcsuite/btcd/btcjson" "math" "strconv" "time" @@ -15,7 +17,6 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/jinzhu/copier" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "go.uber.org/zap" @@ -25,6 +26,11 @@ import ( "github.com/babylonlabs-io/vigilante/types" ) +const ( + changePosition = 1 + dustThreshold btcutil.Amount = 546 +) + type Relayer struct { chainfee.Estimator btcclient.BTCWallet @@ -154,13 +160,7 @@ func (rl *Relayer) shouldResendCheckpoint(ckptInfo *types.CheckpointInfo, bumped // based on the current BTC load, considering both tx sizes // the result is multiplied by ResubmitFeeMultiplier set in config func (rl *Relayer) calculateBumpedFee(ckptInfo *types.CheckpointInfo) btcutil.Amount { - feeRate := rl.getFeeRate() - newTx1Fee := feeRate.FeeForVSize(ckptInfo.Tx1.Size) - newTx2Fee := feeRate.FeeForVSize(ckptInfo.Tx2.Size) - // minus the old fee of the first transaction because we do not want to pay again for the first transaction - bumpedFee := newTx1Fee + newTx2Fee - ckptInfo.Tx1.Fee - - return bumpedFee.MulF64(float64(rl.config.ResubmitFeeMultiplier)) + return ckptInfo.Tx2.Fee.MulF64(rl.config.ResubmitFeeMultiplier) } // resendSecondTxOfCheckpointToBTC resends the second tx of the checkpoint with bumpedFee @@ -168,28 +168,31 @@ func (rl *Relayer) resendSecondTxOfCheckpointToBTC(tx2 *types.BtcTxInfo, bumpedF // set output value of the second tx to be the balance minus the bumped fee // if the bumped fee is higher than the balance, then set the bumped fee to // be equal to the balance to ensure the output value is not negative - balance := tx2.Utxo.Amount + balance := btcutil.Amount(tx2.Tx.TxOut[changePosition].Value) + + // todo: revise this as this means we will end up with output with value 0 that will be rejected by bitcoind as dust output. if bumpedFee > balance { rl.logger.Debugf("the bumped fee %v Satoshis for the second tx is more than UTXO amount %v Satoshis", bumpedFee, balance) bumpedFee = balance } - tx2.Tx.TxOut[1].Value = int64(balance - bumpedFee) + + tx2.Tx.TxOut[changePosition].Value = int64(balance - bumpedFee) // resign the tx as the output is changed - tx, err := rl.dumpPrivKeyAndSignTx(tx2.Tx, tx2.Utxo) + tx, err := rl.signTx(tx2.Tx) if err != nil { return nil, err } - txid, err := rl.sendTxToBTC(tx) + txID, err := rl.sendTxToBTC(tx) if err != nil { return nil, err } // update tx info tx2.Fee = bumpedFee - tx2.TxId = txid + tx2.TxId = txID return tx2, nil } @@ -217,29 +220,22 @@ func (rl *Relayer) calcMinRelayFee(txVirtualSize int64) btcutil.Amount { return minRelayFee } -func (rl *Relayer) dumpPrivKeyAndSignTx(tx *wire.MsgTx, utxo *types.UTXO) (*wire.MsgTx, error) { - // get private key - err := rl.WalletPassphrase(rl.GetWalletPass(), rl.GetWalletLockTime()) - if err != nil { - return nil, err - } - wif, err := rl.DumpPrivKey(utxo.Addr) - if err != nil { +func (rl *Relayer) signTx(tx *wire.MsgTx) (*wire.MsgTx, error) { + // unlock the wallet + if err := rl.WalletPassphrase(rl.GetWalletPass(), rl.GetWalletLockTime()); err != nil { return nil, err } - // add signature/witness depending on the type of the previous address - // if not segwit, add signature; otherwise, add witness - segwit, err := isSegWit(utxo.Addr) + + signedTx, allSigned, err := rl.BTCWallet.SignRawTransactionWithWallet(tx) if err != nil { return nil, err } - // add unlocking script into the input of the tx - tx, err = completeTxIn(tx, segwit, wif.PrivKey, utxo) - if err != nil { - return nil, err + + if !allSigned { + return nil, errors.New("transaction is only partially signed") } - return tx, nil + return signedTx, nil } func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResponse) (*types.CheckpointInfo, error) { @@ -260,18 +256,7 @@ func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResp return nil, err } - utxo, err := rl.PickHighUTXO() - if err != nil { - return nil, err - } - - rl.logger.Debugf("Found one unspent tx with sufficient amount: %v", utxo.TxID) - - tx1, tx2, err := rl.ChainTwoTxAndSend( - utxo, - data1, - data2, - ) + tx1, tx2, err := rl.ChainTwoTxAndSend(data1, data2) if err != nil { return nil, err } @@ -305,20 +290,12 @@ func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResp }, nil } -// ChainTwoTxAndSend consumes one utxo and build two chaining txs: +// ChainTwoTxAndSend builds two chaining txs with the given data: // the second tx consumes the output of the first tx -func (rl *Relayer) ChainTwoTxAndSend( - utxo *types.UTXO, - data1 []byte, - data2 []byte, -) (*types.BtcTxInfo, *types.BtcTxInfo, error) { - +func (rl *Relayer) ChainTwoTxAndSend(data1 []byte, data2 []byte) (*types.BtcTxInfo, *types.BtcTxInfo, error) { // recipient is a change address that all the // remaining balance of the utxo is sent to - tx1, err := rl.buildTxWithData( - utxo, - data1, - ) + tx1, err := rl.buildTxWithData(data1, nil) if err != nil { return nil, nil, fmt.Errorf("failed to add data to tx1: %w", err) } @@ -328,20 +305,9 @@ func (rl *Relayer) ChainTwoTxAndSend( return nil, nil, fmt.Errorf("failed to send tx1 to BTC: %w", err) } - changeUtxo := &types.UTXO{ - TxID: tx1.TxId, - Vout: 1, - ScriptPK: tx1.Tx.TxOut[1].PkScript, - Amount: btcutil.Amount(tx1.Tx.TxOut[1].Value), - Addr: tx1.ChangeAddress, - } - // the second tx consumes the second output (index 1) // of the first tx, as the output at index 0 is OP_RETURN - tx2, err := rl.buildTxWithData( - changeUtxo, - data2, - ) + tx2, err := rl.buildTxWithData(data2, tx1.Tx) if err != nil { return nil, nil, fmt.Errorf("failed to add data to tx2: %w", err) } @@ -356,44 +322,30 @@ func (rl *Relayer) ChainTwoTxAndSend( return tx1, tx2, nil } -// PickHighUTXO picks a UTXO that has the highest amount -func (rl *Relayer) PickHighUTXO() (*types.UTXO, error) { - // get the highest UTXO and UTXOs' sum in the list - topUTXO, sum, err := rl.BTCWallet.GetHighUTXOAndSum() - if err != nil { - return nil, err - } - utxo, err := types.NewUTXO(topUTXO, rl.GetNetParams()) - if err != nil { - return nil, fmt.Errorf("failed to convert ListUnspentResult to UTXO: %w", err) - } - rl.logger.Debugf("pick utxo with id: %v, amount: %v, confirmations: %v", utxo.TxID, utxo.Amount, topUTXO.Confirmations) - - // record metrics of UTXOs' sum - rl.metrics.AvailableBTCBalance.Set(sum) - - return utxo, nil -} - -// buildTxWithData builds a tx with data inserted as OP_RETURN -// note that OP_RETURN is set as the first output of the tx (index 0) -// and the rest of the balance is sent to a new change address -// as the second output with index 1 -func (rl *Relayer) buildTxWithData( - utxo *types.UTXO, - data []byte, -) (*types.BtcTxInfo, error) { - rl.logger.Debugf("Building a BTC tx using %v with data %x", utxo.TxID.String(), data) +// buildTxWithData constructs a Bitcoin transaction with custom data inserted as an OP_RETURN output. +// If `firstTx` is provided, it uses its transaction ID and a predefined output index (`changePosition`) +// to create an input for the new transaction. The OP_RETURN output is added as the first output (index 0). +// +// This function also ensures that the transaction fee is sufficient and signs the transaction before returning it. +// If the UTXO value is insufficient to cover the fee or if the change amount falls below the dust threshold, +// an error is returned. +// +// Parameters: +// - data: The custom data to be inserted into the transaction as an OP_RETURN output. +// - firstTx: An optional transaction used to create an input for the new transaction. +func (rl *Relayer) buildTxWithData(data []byte, firstTx *wire.MsgTx) (*types.BtcTxInfo, error) { tx := wire.NewMsgTx(wire.TxVersion) - outPoint := wire.NewOutPoint(utxo.TxID, utxo.Vout) - txIn := wire.NewTxIn(outPoint, nil, nil) - // Enable replace-by-fee - // See https://river.com/learn/terms/r/replace-by-fee-rbf - txIn.Sequence = math.MaxUint32 - 2 - tx.AddTxIn(txIn) + if firstTx != nil { + txID := firstTx.TxHash() + outPoint := wire.NewOutPoint(&txID, changePosition) + txIn := wire.NewTxIn(outPoint, nil, nil) + // Enable replace-by-fee, see https://river.com/learn/terms/r/replace-by-fee-rbf + txIn.Sequence = math.MaxUint32 - 2 + tx.AddTxIn(txIn) + } - // build txout for data + // build txOut for data builder := txscript.NewScriptBuilder() dataScript, err := builder.AddOp(txscript.OP_RETURN).AddData(data).Script() if err != nil { @@ -401,61 +353,79 @@ func (rl *Relayer) buildTxWithData( } tx.AddTxOut(wire.NewTxOut(0, dataScript)) - // build txout for change - changeAddr, err := rl.GetChangeAddress() - if err != nil { - return nil, fmt.Errorf("failed to get change address: %w", err) - } - rl.logger.Debugf("Got a change address %v", changeAddr.String()) - changeScript, err := txscript.PayToAddrScript(changeAddr) + changePosition := 1 // must declare here as you cannot take address of const needed bellow + feeRate := btcutil.Amount(rl.getFeeRate()).ToBTC() + rawTxResult, err := rl.BTCWallet.FundRawTransaction(tx, btcjson.FundRawTransactionOpts{ + FeeRate: &feeRate, + ChangePosition: &changePosition, + }, nil) if err != nil { return nil, err } - copiedTx := &wire.MsgTx{} - err = copier.Copy(copiedTx, tx) + + rl.logger.Debugf("Building a BTC tx using %s with data %x", rawTxResult.Transaction.TxID(), data) + + _, addresses, _, err := txscript.ExtractPkScriptAddrs( + rawTxResult.Transaction.TxOut[changePosition].PkScript, + rl.GetNetParams(), + ) + if err != nil { return nil, err } - txSize, err := calculateTxVirtualSize(copiedTx, utxo, changeScript) + + if len(addresses) == 0 { + return nil, errors.New("no change address found") + } + + changeAddr := addresses[0] + rl.logger.Debugf("Got a change address %v", changeAddr.String()) + + txSize, err := calculateTxVirtualSize(rawTxResult.Transaction) if err != nil { return nil, err } + + changeAmount := btcutil.Amount(rawTxResult.Transaction.TxOut[changePosition].Value) minRelayFee := rl.calcMinRelayFee(txSize) - if utxo.Amount < minRelayFee { - return nil, fmt.Errorf("the value of the utxo is not sufficient for relaying the tx. Require: %v. Have: %v", minRelayFee, utxo.Amount) + + if changeAmount < minRelayFee { + return nil, fmt.Errorf("the value of the utxo is not sufficient for relaying the tx. Require: %v. Have: %v", minRelayFee, changeAmount) } - txFee := rl.getFeeRate().FeeForVSize(txSize) + + txFee := rawTxResult.Fee // ensuring the tx fee is not lower than the minimum relay fee if txFee < minRelayFee { txFee = minRelayFee } // ensuring the tx fee is not higher than the utxo value - if utxo.Amount < txFee { - return nil, fmt.Errorf("the value of the utxo is not sufficient for paying the calculated fee of the tx. Calculated: %v. Have: %v", txFee, utxo.Amount) + if changeAmount < txFee { + return nil, fmt.Errorf("the value of the utxo is not sufficient for paying the calculated fee of the tx. Calculated: %v. Have: %v", txFee, changeAmount) } - change := utxo.Amount - txFee - tx.AddTxOut(wire.NewTxOut(int64(change), changeScript)) // sign tx - tx, err = rl.dumpPrivKeyAndSignTx(tx, utxo) + tx, err = rl.signTx(rawTxResult.Transaction) if err != nil { return nil, fmt.Errorf("failed to sign tx: %w", err) } // serialization var signedTxBytes bytes.Buffer - err = tx.Serialize(&signedTxBytes) - if err != nil { + if err := tx.Serialize(&signedTxBytes); err != nil { return nil, err } - rl.logger.Debugf("Successfully composed a BTC tx with balance of input: %v, "+ - "tx fee: %v, output value: %v, tx size: %v, hex: %v", - utxo.Amount, txFee, change, txSize, hex.EncodeToString(signedTxBytes.Bytes())) + change := changeAmount - txFee + + if change < dustThreshold { + return nil, fmt.Errorf("change amount is %v less then dust treshold %v", change, dustThreshold) + } + + rl.logger.Debugf("Successfully composed a BTC tx: tx fee: %v, output value: %v, tx size: %v, hex: %v", + txFee, changeAmount, txSize, hex.EncodeToString(signedTxBytes.Bytes())) return &types.BtcTxInfo{ Tx: tx, - Utxo: utxo, ChangeAddress: changeAddr, Size: txSize, Fee: txFee, @@ -490,10 +460,12 @@ func (rl *Relayer) getFeeRate() chainfee.SatPerKVByte { func (rl *Relayer) sendTxToBTC(tx *wire.MsgTx) (*chainhash.Hash, error) { rl.logger.Debugf("Sending tx %v to BTC", tx.TxHash().String()) + ha, err := rl.SendRawTransaction(tx, true) if err != nil { return nil, err } rl.logger.Debugf("Successfully sent tx %v to BTC", tx.TxHash().String()) + return ha, nil } diff --git a/submitter/relayer/utils.go b/submitter/relayer/utils.go index 7f267592..c909ac25 100644 --- a/submitter/relayer/utils.go +++ b/submitter/relayer/utils.go @@ -1,100 +1,16 @@ package relayer import ( - "bytes" "errors" - - "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/mempool" - "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - secp "github.com/decred/dcrd/dcrec/secp256k1/v4" - - "github.com/babylonlabs-io/vigilante/types" ) -func isSegWit(addr btcutil.Address) (bool, error) { - switch addr.(type) { - case *btcutil.AddressPubKeyHash, *btcutil.AddressScriptHash, *btcutil.AddressPubKey: - return false, nil - case *btcutil.AddressWitnessPubKeyHash, *btcutil.AddressWitnessScriptHash: - return true, nil - default: - return false, errors.New("non-supported address type") - } -} - -func calculateTxVirtualSize(tx *wire.MsgTx, utxo *types.UTXO, changeScript []byte) (int64, error) { - tx.AddTxOut(wire.NewTxOut(int64(utxo.Amount), changeScript)) - - // when calculating tx size we can use a random private key - privKey, err := secp.GeneratePrivateKey() - if err != nil { - return 0, err - } - - // add signature/witness depending on the type of the previous address - // if not segwit, add signature; otherwise, add witness - segwit, err := isSegWit(utxo.Addr) - if err != nil { - return 0, err - } - - tx, err = completeTxIn(tx, segwit, privKey, utxo) - if err != nil { - return 0, err - } - - var txBytes bytes.Buffer - err = tx.Serialize(&txBytes) - if err != nil { - return 0, err - } - btcTx, err := btcutil.NewTxFromBytes(txBytes.Bytes()) - if err != nil { - return 0, err - } - - return mempool.GetTxVirtualSize(btcTx), err -} - -func completeTxIn(tx *wire.MsgTx, isSegWit bool, privKey *btcec.PrivateKey, utxo *types.UTXO) (*wire.MsgTx, error) { - if !isSegWit { - sig, err := txscript.SignatureScript( - tx, - 0, - utxo.ScriptPK, - txscript.SigHashAll, - privKey, - true, - ) - if err != nil { - return nil, err - } - tx.TxIn[0].SignatureScript = sig - } else { - sighashes := txscript.NewTxSigHashes( - tx, - // Use the CannedPrevOutputFetcher which is only able to return information about a single UTXO - // See https://github.com/btcsuite/btcd/commit/e781b66e2fb9a354a14bfa7fbdd44038450cc13f - // for details on the output fetchers - txscript.NewCannedPrevOutputFetcher(utxo.ScriptPK, int64(utxo.Amount))) - wit, err := txscript.WitnessSignature( - tx, - sighashes, - 0, - int64(utxo.Amount), - utxo.ScriptPK, - txscript.SigHashAll, - privKey, - true, - ) - if err != nil { - return nil, err - } - tx.TxIn[0].Witness = wit +func calculateTxVirtualSize(tx *wire.MsgTx) (int64, error) { + if tx == nil { + return -1, errors.New("tx param nil") } - return tx, nil + return mempool.GetTxVirtualSize(btcutil.NewTx(tx)), nil } diff --git a/testutil/mocks/btcclient.go b/testutil/mocks/btcclient.go index 8023d652..1bbdb6b9 100644 --- a/testutil/mocks/btcclient.go +++ b/testutil/mocks/btcclient.go @@ -251,6 +251,21 @@ func (mr *MockBTCWalletMockRecorder) DumpPrivKey(address interface{}) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DumpPrivKey", reflect.TypeOf((*MockBTCWallet)(nil).DumpPrivKey), address) } +// 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() @@ -383,6 +398,22 @@ func (mr *MockBTCWalletMockRecorder) SendRawTransaction(tx, allowHighFees interf 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() diff --git a/types/ckpt_info.go b/types/ckpt_info.go index 4c9b93fc..5d64c6b0 100644 --- a/types/ckpt_info.go +++ b/types/ckpt_info.go @@ -21,7 +21,6 @@ type BtcTxInfo struct { TxId *chainhash.Hash Tx *wire.MsgTx ChangeAddress btcutil.Address - Utxo *UTXO // the UTXO used to build this BTC tx Size int64 // the size of the BTC tx Fee btcutil.Amount // tx fee cost by the BTC tx } From cf21c32f95e86201c8e7b6ab3a5a063d62285122 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Wed, 21 Aug 2024 11:37:40 +0200 Subject: [PATCH 07/12] chore(go): upgrade go to 1.23 (#18) - Upgrades go to 1.23 --- .github/workflows/ci.yml | 2 ++ .github/workflows/publish.yml | 2 ++ Dockerfile | 2 +- README.md | 2 +- go.mod | 6 ++---- monitor/monitor.go | 11 +++++++---- tools/go.mod | 4 +--- types/epoch_info.go | 6 ++---- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8dc848bc..ea7652c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,8 @@ jobs: lint_test: uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.3.1 with: + go-version: '1.23' + go-lint-version: 'v1.60.2' run-unit-tests: true run-integration-tests: true run-lint: true diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b49b58f6..e96ee865 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,6 +12,8 @@ jobs: lint_test: uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.3.1 with: + go-version: '1.23' + go-lint-version: 'v1.60.2' run-unit-tests: true run-integration-tests: true run-lint: true diff --git a/Dockerfile b/Dockerfile index 449db259..dd8a3271 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ## Image for building -FROM golang:1.21-alpine AS build-env +FROM golang:1.23-alpine AS build-env # TARGETPLATFORM should be one of linux/amd64 or linux/arm64. diff --git a/README.md b/README.md index 5eb1348d..d2f369e9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ There are four vigilante programs: ## Requirements -- Go 1.21 +- Go 1.23 - Package [libzmq](https://github.com/zeromq/libzmq) - [btcd](https://github.com/btcsuite/btcd/tree/master?tab=readme-ov-file#installation) binaries (only for testing) diff --git a/go.mod b/go.mod index 4db3aa96..7456b18f 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/babylonlabs-io/vigilante -go 1.21 - -toolchain go1.21.4 +go 1.23 require ( cosmossdk.io/errors v1.0.1 @@ -19,7 +17,6 @@ require ( github.com/cometbft/cometbft v0.38.7 github.com/cosmos/cosmos-sdk v0.50.6 github.com/cosmos/relayer/v2 v2.5.1 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/gogo/protobuf v1.3.3 github.com/golang/mock v1.6.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 @@ -118,6 +115,7 @@ require ( github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/decred/dcrd/lru v1.0.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect diff --git a/monitor/monitor.go b/monitor/monitor.go index a6d13824..e3fd7fff 100644 --- a/monitor/monitor.go +++ b/monitor/monitor.go @@ -197,8 +197,10 @@ func (m *Monitor) GetCurrentEpoch() uint64 { func (m *Monitor) VerifyCheckpoint(btcCkpt *checkpointingtypes.RawCheckpoint) error { // check whether the epoch number of the checkpoint equals to the current epoch number if m.GetCurrentEpoch() != btcCkpt.EpochNum { - return errors.Wrapf(types.ErrInvalidEpochNum, fmt.Sprintf("found a checkpoint with epoch %v, but the monitor expects epoch %v", - btcCkpt.EpochNum, m.GetCurrentEpoch())) + return errors.Wrapf(types.ErrInvalidEpochNum, + "found a checkpoint with epoch %v, but the monitor expects epoch %v", + btcCkpt.EpochNum, m.GetCurrentEpoch()) + } // verify BLS sig of the BTC checkpoint err := m.curEpoch.VerifyMultiSig(btcCkpt) @@ -221,8 +223,9 @@ func (m *Monitor) VerifyCheckpoint(btcCkpt *checkpointingtypes.RawCheckpoint) er } // check whether the checkpoint from Babylon has the same BlockHash of the BTC checkpoint if !ckpt.BlockHash.Equal(*btcCkpt.BlockHash) { - return errors.Wrapf(types.ErrInconsistentBlockHash, fmt.Sprintf("Babylon checkpoint's BlockHash %s, BTC checkpoint's BlockHash %s", - ckpt.BlockHash.String(), btcCkpt.BlockHash)) + return errors.Wrapf(types.ErrInconsistentBlockHash, + "Babylon checkpoint's BlockHash %s, BTC checkpoint's BlockHash %s", + ckpt.BlockHash.String(), btcCkpt.BlockHash) } return nil } diff --git a/tools/go.mod b/tools/go.mod index ce29ae53..6f7f46dc 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,8 +1,6 @@ module github.com/babylonlabs-io/vigilante/tools -go 1.21 - -toolchain go1.21.4 +go 1.23 require github.com/babylonlabs-io/babylon v0.9.1 diff --git a/types/epoch_info.go b/types/epoch_info.go index b4d74559..7e2a6121 100644 --- a/types/epoch_info.go +++ b/types/epoch_info.go @@ -2,8 +2,6 @@ package types import ( "bytes" - "fmt" - "github.com/babylonlabs-io/babylon/crypto/bls12381" ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" "github.com/boljen/go-bitmap" @@ -65,10 +63,10 @@ func (ei *EpochInfo) Equal(epochInfo *EpochInfo) bool { func (ei *EpochInfo) VerifyMultiSig(ckpt *ckpttypes.RawCheckpoint) error { signerKeySet, sumPower, err := ei.GetSignersKeySetWithPowerSum(ckpt.Bitmap) if sumPower*3 <= ei.GetTotalPower()*2 { - return errors.Wrapf(ErrInsufficientPower, fmt.Sprintf("expected to be greater than %v, got %v", ei.GetTotalPower()*2/3, sumPower)) + return errors.Wrapf(ErrInsufficientPower, "expected to be greater than %v, got %v", ei.GetTotalPower()*2/3, sumPower) } if err != nil { - return errors.Wrapf(ErrInvalidMultiSig, fmt.Sprintf("failed to get signer set: %s", err.Error())) + return errors.Wrapf(ErrInvalidMultiSig, "failed to get signer set: %s", err.Error()) } msgBytes := ckpt.SignedMsg() valid, err := bls12381.VerifyMultiSig(*ckpt.BlsMultiSig, signerKeySet, msgBytes) From e9922316a01888f6f503d63fdb428394f998eb28 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:22:33 +0200 Subject: [PATCH 08/12] chore(btcclient): use notifier interface (#20) - Removes usage of btc client with subscription to zmq - Removes proprietary client implementation of zmq subscription as we now rely on lnd's References [issue](https://github.com/babylonchain/vigilante/issues/199) --- btcclient/client.go | 22 +-- btcclient/client_block_subscriber.go | 139 ------------------ btcclient/client_wallet.go | 12 +- btcclient/interface.go | 3 - btcclient/notifier.go | 16 +++ btcclient/testutils.go | 25 ---- cmd/vigilante/cmd/btcstaking_tracker.go | 18 +-- cmd/vigilante/cmd/monitor.go | 26 ++-- cmd/vigilante/cmd/reporter.go | 10 +- e2etest/reporter_e2e_test.go | 25 ++++ e2etest/test_manager.go | 2 +- go.mod | 9 +- go.sum | 8 +- monitor/btcscanner/block_handler.go | 112 +++++++-------- monitor/btcscanner/btc_scanner.go | 31 ++-- monitor/btcscanner/btc_scanner_test.go | 9 +- monitor/monitor.go | 3 + reporter/block_handler.go | 160 ++++++--------------- reporter/bootstrapping.go | 19 +-- reporter/reorg_list.go | 76 ---------- reporter/reporter.go | 21 ++- reporter/utils.go | 10 -- reporter/utils_test.go | 3 + testutil/mocks/btcclient.go | 41 ------ zmq/client.go | 129 ----------------- zmq/subscribe.go | 182 ------------------------ 26 files changed, 233 insertions(+), 878 deletions(-) delete mode 100644 btcclient/client_block_subscriber.go delete mode 100644 btcclient/testutils.go delete mode 100644 reporter/reorg_list.go delete mode 100644 zmq/client.go delete mode 100644 zmq/subscribe.go diff --git a/btcclient/client.go b/btcclient/client.go index 4cd3933f..8da20dcd 100644 --- a/btcclient/client.go +++ b/btcclient/client.go @@ -15,8 +15,6 @@ import ( "go.uber.org/zap" "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/types" - "github.com/babylonlabs-io/vigilante/zmq" ) var _ BTCClient = &Client{} @@ -25,18 +23,14 @@ var _ BTCClient = &Client{} // for information regarding the current best block chain. type Client struct { *rpcclient.Client - zmqClient *zmq.Client - Params *chaincfg.Params - Cfg *config.BTCConfig + params *chaincfg.Params + cfg *config.BTCConfig logger *zap.SugaredLogger // retry attributes retrySleepTime time.Duration maxRetrySleepTime time.Duration - - // channel for notifying new BTC blocks to reporter - blockEventChan chan *types.BlockEvent } func (c *Client) GetTipBlockVerbose() (*btcjson.GetBlockVerboseResult, error) { @@ -54,16 +48,4 @@ func (c *Client) GetTipBlockVerbose() (*btcjson.GetBlockVerboseResult, error) { func (c *Client) Stop() { c.Shutdown() - // NewWallet will create a client with nil blockEventChan, - // while NewWithBlockSubscriber will have a non-nil one, so - // we need to check here - if c.blockEventChan != nil { - close(c.blockEventChan) - } - - if c.zmqClient != nil { - if err := c.zmqClient.Close(); err != nil { - c.logger.Debug(err) - } - } } diff --git a/btcclient/client_block_subscriber.go b/btcclient/client_block_subscriber.go deleted file mode 100644 index c3245158..00000000 --- a/btcclient/client_block_subscriber.go +++ /dev/null @@ -1,139 +0,0 @@ -package btcclient - -import ( - "fmt" - "time" - - "github.com/babylonlabs-io/babylon/types/retry" - "github.com/btcsuite/btcd/btcutil" - "go.uber.org/zap" - - "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/netparams" - "github.com/babylonlabs-io/vigilante/types" - "github.com/babylonlabs-io/vigilante/zmq" - - "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/wire" -) - -// NewWithBlockSubscriber creates a new BTC client that subscribes to newly connected/disconnected blocks -// used by vigilant reporter -func NewWithBlockSubscriber(cfg *config.BTCConfig, retrySleepTime, maxRetrySleepTime time.Duration, parentLogger *zap.Logger) (*Client, error) { - client := &Client{} - params, err := netparams.GetBTCParams(cfg.NetParams) - if err != nil { - return nil, err - } - client.blockEventChan = make(chan *types.BlockEvent, 10000) // TODO: parameterise buffer size - client.Cfg = cfg - client.Params = params - logger := parentLogger.With(zap.String("module", "btcclient")) - client.logger = logger.Sugar() - - client.retrySleepTime = retrySleepTime - client.maxRetrySleepTime = maxRetrySleepTime - - switch cfg.BtcBackend { - case types.Bitcoind: - // TODO Currently we are not using Params field of rpcclient.ConnConfig due to bug in btcd - // when handling signet. - connCfg := &rpcclient.ConnConfig{ - Host: cfg.Endpoint, - HTTPPostMode: true, - User: cfg.Username, - Pass: cfg.Password, - DisableTLS: cfg.DisableClientTLS, - } - - rpcClient, err := rpcclient.New(connCfg, nil) - if err != nil { - return nil, err - } - - zmqClient, err := zmq.New(logger, cfg.ZmqSeqEndpoint, client.blockEventChan, rpcClient) - if err != nil { - return nil, err - } - - client.zmqClient = zmqClient - client.Client = rpcClient - case types.Btcd: - notificationHandlers := rpcclient.NotificationHandlers{ - OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) { - client.logger.Debugf("Block %v at height %d has been connected at time %v", header.BlockHash(), height, header.Timestamp) - client.blockEventChan <- types.NewBlockEvent(types.BlockConnected, height, header) - }, - OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { - client.logger.Debugf("Block %v at height %d has been disconnected at time %v", header.BlockHash(), height, header.Timestamp) - client.blockEventChan <- types.NewBlockEvent(types.BlockDisconnected, height, header) - }, - } - - // TODO Currently we are not using Params field of rpcclient.ConnConfig due to bug in btcd - // when handling signet. - connCfg := &rpcclient.ConnConfig{ - Host: cfg.Endpoint, - Endpoint: "ws", // websocket - User: cfg.Username, - Pass: cfg.Password, - DisableTLS: cfg.DisableClientTLS, - Certificates: cfg.ReadCAFile(), - } - - rpcClient, err := rpcclient.New(connCfg, ¬ificationHandlers) - if err != nil { - return nil, err - } - - // ensure we are using btcd as Bitcoin node, since Websocket-based subscriber is only available in btcd - backend, err := rpcClient.BackendVersion() - if err != nil { - return nil, fmt.Errorf("failed to get BTC backend: %v", err) - } - if backend != rpcclient.BtcdPost2401 { - return nil, fmt.Errorf("websocket is only supported by btcd, but got %v", backend) - } - - client.Client = rpcClient - } - - client.logger.Info("Successfully created the BTC client and connected to the BTC server") - - return client, nil -} - -func (c *Client) subscribeBlocksByWebSocket() error { - if err := c.NotifyBlocks(); err != nil { - return err - } - c.logger.Info("Successfully subscribed to newly connected/disconnected blocks via WebSocket") - return nil -} - -func (c *Client) mustSubscribeBlocksByWebSocket() { - if err := retry.Do(c.retrySleepTime, c.maxRetrySleepTime, func() error { - return c.subscribeBlocksByWebSocket() - }); err != nil { - panic(err) - } -} - -func (c *Client) mustSubscribeBlocksByZmq() { - if err := c.zmqClient.SubscribeSequence(); err != nil { - panic(err) - } -} - -func (c *Client) MustSubscribeBlocks() { - switch c.Cfg.BtcBackend { - case types.Btcd: - c.mustSubscribeBlocksByWebSocket() - case types.Bitcoind: - c.mustSubscribeBlocksByZmq() - } -} - -func (c *Client) BlockEventChan() <-chan *types.BlockEvent { - return c.blockEventChan -} diff --git a/btcclient/client_wallet.go b/btcclient/client_wallet.go index fe52e740..16c964bd 100644 --- a/btcclient/client_wallet.go +++ b/btcclient/client_wallet.go @@ -26,8 +26,8 @@ func NewWallet(cfg *config.BTCConfig, parentLogger *zap.Logger) (*Client, error) return nil, err } wallet := &Client{} - wallet.Cfg = cfg - wallet.Params = params + wallet.cfg = cfg + wallet.params = params wallet.logger = parentLogger.With(zap.String("module", "btcclient_wallet")).Sugar() connCfg := &rpcclient.ConnConfig{} @@ -69,15 +69,15 @@ func NewWallet(cfg *config.BTCConfig, parentLogger *zap.Logger) (*Client, error) } func (c *Client) GetWalletPass() string { - return c.Cfg.WalletPassword + return c.cfg.WalletPassword } func (c *Client) GetWalletLockTime() int64 { - return c.Cfg.WalletLockTime + return c.cfg.WalletLockTime } func (c *Client) GetNetParams() *chaincfg.Params { - net, err := netparams.GetBTCParams(c.Cfg.NetParams) + net, err := netparams.GetBTCParams(c.cfg.NetParams) if err != nil { panic(fmt.Errorf("failed to get BTC network params: %w", err)) } @@ -85,7 +85,7 @@ func (c *Client) GetNetParams() *chaincfg.Params { } func (c *Client) GetBTCConfig() *config.BTCConfig { - return c.Cfg + return c.cfg } func (c *Client) ListUnspent() ([]btcjson.ListUnspentResult, error) { diff --git a/btcclient/interface.go b/btcclient/interface.go index d3021412..71b9bca5 100644 --- a/btcclient/interface.go +++ b/btcclient/interface.go @@ -14,8 +14,6 @@ import ( type BTCClient interface { Stop() WaitForShutdown() - MustSubscribeBlocks() - BlockEventChan() <-chan *types.BlockEvent GetBestBlock() (*chainhash.Hash, uint64, error) GetBlockByHash(blockHash *chainhash.Hash) (*types.IndexedBlock, *wire.MsgBlock, error) FindTailBlocksByHeight(height uint64) ([]*types.IndexedBlock, error) @@ -37,7 +35,6 @@ type BTCWallet interface { SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) GetRawChangeAddress(account string) (btcutil.Address, error) WalletPassphrase(passphrase string, timeoutSecs int64) error - DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) GetHighUTXOAndSum() (*btcjson.ListUnspentResult, float64, error) FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) SignRawTransactionWithWallet(tx *wire.MsgTx) (*wire.MsgTx, bool, error) diff --git a/btcclient/notifier.go b/btcclient/notifier.go index 4bc73c71..2ecc10ca 100644 --- a/btcclient/notifier.go +++ b/btcclient/notifier.go @@ -3,6 +3,7 @@ package btcclient import ( "encoding/hex" "fmt" + "github.com/babylonlabs-io/vigilante/netparams" "net" "os" "time" @@ -248,3 +249,18 @@ func NewNodeBackend( return nil, fmt.Errorf("unknown node backend: %v", cfg.ActiveNodeBackend) } } + +// NewNodeBackendWithParams creates a new NodeBackend by incorporating parameter retrieval and config conversion. +func NewNodeBackendWithParams(cfg config.BTCConfig, rawCert string) (*NodeBackend, error) { + btcParams, err := netparams.GetBTCParams(cfg.NetParams) + if err != nil { + return nil, fmt.Errorf("failed to get BTC net params: %w", err) + } + + btcNotifier, err := NewNodeBackend(CfgToBtcNodeBackendConfig(cfg, rawCert), btcParams, &EmptyHintCache{}) + if err != nil { + return nil, fmt.Errorf("failed to initialize notifier: %w", err) + } + + return btcNotifier, nil +} diff --git a/btcclient/testutils.go b/btcclient/testutils.go deleted file mode 100644 index c0941e58..00000000 --- a/btcclient/testutils.go +++ /dev/null @@ -1,25 +0,0 @@ -package btcclient - -import ( - "time" - - "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/netparams" - "github.com/babylonlabs-io/vigilante/types" - "github.com/btcsuite/btcd/rpcclient" -) - -func NewTestClientWithWsSubscriber(rpcClient *rpcclient.Client, cfg *config.BTCConfig, retrySleepTime time.Duration, maxRetrySleepTime time.Duration, blockEventChan chan *types.BlockEvent) (*Client, error) { - net, err := netparams.GetBTCParams(cfg.NetParams) - if err != nil { - return nil, err - } - return &Client{ - Client: rpcClient, - Params: net, - Cfg: cfg, - retrySleepTime: retrySleepTime, - maxRetrySleepTime: maxRetrySleepTime, - blockEventChan: blockEventChan, - }, nil -} diff --git a/cmd/vigilante/cmd/btcstaking_tracker.go b/cmd/vigilante/cmd/btcstaking_tracker.go index f11bf804..d4794170 100644 --- a/cmd/vigilante/cmd/btcstaking_tracker.go +++ b/cmd/vigilante/cmd/btcstaking_tracker.go @@ -8,7 +8,6 @@ import ( bst "github.com/babylonlabs-io/vigilante/btcstaking-tracker" "github.com/babylonlabs-io/vigilante/config" "github.com/babylonlabs-io/vigilante/metrics" - "github.com/babylonlabs-io/vigilante/netparams" "github.com/babylonlabs-io/vigilante/rpcserver" "github.com/spf13/cobra" ) @@ -61,26 +60,17 @@ func GetBTCStakingTracker() *cobra.Command { // create BTC client and connect to BTC server // Note that monitor needs to subscribe to new BTC blocks - btcClient, err := btcclient.NewWithBlockSubscriber( - &cfg.BTC, - cfg.Common.RetrySleepTime, - cfg.Common.MaxRetrySleepTime, - rootLogger, - ) + btcClient, err := btcclient.NewWallet(&cfg.BTC, rootLogger) + if err != nil { panic(fmt.Errorf("failed to open BTC client: %w", err)) } // create BTC notifier // TODO: is it possible to merge BTC client and BTC notifier? - btcParams, err := netparams.GetBTCParams(cfg.BTC.NetParams) + btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC, "") if err != nil { - panic(fmt.Errorf("failed to get BTC parameter: %w", err)) - } - btcCfg := btcclient.CfgToBtcNodeBackendConfig(cfg.BTC, "") // we will read certifcates from file - btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) - if err != nil { - panic(fmt.Errorf("failed to create btc chain notifier: %w", err)) + panic(err) } bsMetrics := metrics.NewBTCStakingTrackerMetrics() diff --git a/cmd/vigilante/cmd/monitor.go b/cmd/vigilante/cmd/monitor.go index 2b66c15b..49259c50 100644 --- a/cmd/vigilante/cmd/monitor.go +++ b/cmd/vigilante/cmd/monitor.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - bbnqccfg "github.com/babylonlabs-io/babylon/client/config" bbnqc "github.com/babylonlabs-io/babylon/client/query" "github.com/spf13/cobra" @@ -62,13 +61,7 @@ func GetMonitorCmd() *cobra.Command { } // create BTC client and connect to BTC server - // Note that monitor needs to subscribe to new BTC blocks - btcClient, err = btcclient.NewWithBlockSubscriber( - &cfg.BTC, - cfg.Common.RetrySleepTime, - cfg.Common.MaxRetrySleepTime, - rootLogger, - ) + btcClient, err = btcclient.NewWallet(&cfg.BTC, rootLogger) if err != nil { panic(fmt.Errorf("failed to open BTC client: %w", err)) } @@ -80,8 +73,23 @@ func GetMonitorCmd() *cobra.Command { // register monitor metrics monitorMetrics := metrics.NewMonitorMetrics() + // create the chain notifier + btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC, "") + if err != nil { + panic(err) + } + // create monitor - vigilanteMonitor, err = monitor.New(&cfg.Monitor, &cfg.Common, rootLogger, genesisInfo, bbnQueryClient, btcClient, monitorMetrics) + vigilanteMonitor, err = monitor.New( + &cfg.Monitor, + &cfg.Common, + rootLogger, + genesisInfo, + bbnQueryClient, + btcClient, + btcNotifier, + monitorMetrics, + ) if err != nil { panic(fmt.Errorf("failed to create vigilante monitor: %w", err)) } diff --git a/cmd/vigilante/cmd/reporter.go b/cmd/vigilante/cmd/reporter.go index bbaa9317..5c9a0b60 100644 --- a/cmd/vigilante/cmd/reporter.go +++ b/cmd/vigilante/cmd/reporter.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - bbnclient "github.com/babylonlabs-io/babylon/client/client" "github.com/spf13/cobra" @@ -48,7 +47,7 @@ func GetReporterCmd() *cobra.Command { // create BTC client and connect to BTC server // Note that vigilant reporter needs to subscribe to new BTC blocks - btcClient, err = btcclient.NewWithBlockSubscriber(&cfg.BTC, cfg.Common.RetrySleepTime, cfg.Common.MaxRetrySleepTime, rootLogger) + btcClient, err = btcclient.NewWallet(&cfg.BTC, rootLogger) if err != nil { panic(fmt.Errorf("failed to open BTC client: %w", err)) } @@ -62,12 +61,19 @@ func GetReporterCmd() *cobra.Command { // register reporter metrics reporterMetrics := metrics.NewReporterMetrics() + // create the chain notifier + btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC, "") + if err != nil { + panic(err) + } + // create reporter vigilantReporter, err = reporter.New( &cfg.Reporter, rootLogger, btcClient, babylonClient, + btcNotifier, cfg.Common.RetrySleepTime, cfg.Common.MaxRetrySleepTime, reporterMetrics, diff --git a/e2etest/reporter_e2e_test.go b/e2etest/reporter_e2e_test.go index b269832c..9fec7d97 100644 --- a/e2etest/reporter_e2e_test.go +++ b/e2etest/reporter_e2e_test.go @@ -4,6 +4,8 @@ package e2etest import ( + "github.com/babylonlabs-io/vigilante/btcclient" + "github.com/babylonlabs-io/vigilante/netparams" "sync" "testing" "time" @@ -60,11 +62,19 @@ func TestReporter_BoostrapUnderFrequentBTCHeaders(t *testing.T) { reporterMetrics := metrics.NewReporterMetrics() + // create the chain notifier + btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) + require.NoError(t, err) + btcCfg := btcclient.CfgToBtcNodeBackendConfig(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, @@ -116,11 +126,18 @@ func TestRelayHeadersAndHandleRollbacks(t *testing.T) { reporterMetrics := metrics.NewReporterMetrics() + btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) + require.NoError(t, err) + btcCfg := btcclient.CfgToBtcNodeBackendConfig(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, @@ -160,11 +177,18 @@ func TestHandleReorgAfterRestart(t *testing.T) { reporterMetrics := metrics.NewReporterMetrics() + btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) + require.NoError(t, err) + btcCfg := btcclient.CfgToBtcNodeBackendConfig(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, @@ -199,6 +223,7 @@ func TestHandleReorgAfterRestart(t *testing.T) { logger, btcClient, tm.BabylonClient, + btcNotifier, tm.Config.Common.RetrySleepTime, tm.Config.Common.MaxRetrySleepTime, reporterMetrics, diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index 7a82624e..e7e2b003 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -65,7 +65,7 @@ type TestManager struct { } func initBTCClientWithSubscriber(t *testing.T, cfg *config.Config) *btcclient.Client { - client, err := btcclient.NewWithBlockSubscriber(&cfg.BTC, cfg.Common.RetrySleepTime, cfg.Common.MaxRetrySleepTime, zap.NewNop()) + client, err := btcclient.NewWallet(&cfg.BTC, zap.NewNop()) require.NoError(t, err) // let's wait until chain rpc becomes available diff --git a/go.mod b/go.mod index 7456b18f..fb2d502d 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,6 @@ require ( github.com/jsternberg/zap-logfmt v1.3.0 github.com/lightningnetwork/lnd v0.16.4-beta.rc1 github.com/ory/dockertest/v3 v3.9.1 - github.com/pebbe/zmq4 v1.2.9 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.0 github.com/spf13/cobra v1.8.0 @@ -68,6 +67,7 @@ require ( 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/chacha20 v0.0.0-20180709150244-8b13a72661da // 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 @@ -75,7 +75,11 @@ require ( github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/btcsuite/btcd/btcutil/psbt v1.1.8 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect + github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 // indirect + github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 // indirect + github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 // indirect github.com/btcsuite/btcwallet/walletdb v1.4.0 // indirect github.com/btcsuite/btcwallet/wtxmgr v1.5.0 // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect @@ -137,6 +141,7 @@ require ( github.com/fergusstrange/embedded-postgres v1.10.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect @@ -209,6 +214,7 @@ require ( github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect github.com/lightninglabs/neutrino v0.15.0 // indirect github.com/lightninglabs/neutrino/cache v1.1.1 // indirect + github.com/lightningnetwork/lightning-onion v1.2.1-0.20221202012345-ca23184850a1 // indirect github.com/lightningnetwork/lnd/clock v1.1.0 // indirect github.com/lightningnetwork/lnd/healthcheck v1.2.2 // indirect github.com/lightningnetwork/lnd/kvdb v1.4.1 // indirect @@ -217,6 +223,7 @@ require ( github.com/lightningnetwork/lnd/tlv v1.1.0 // indirect github.com/lightningnetwork/lnd/tor v1.1.0 // indirect github.com/linxGnu/grocksdb v1.8.14 // indirect + github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index ffc3ea90..8ff2ee2b 100644 --- a/go.sum +++ b/go.sum @@ -303,16 +303,19 @@ github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13P 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= github.com/btcsuite/btcd v0.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= +github.com/btcsuite/btcd v0.23.1/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd v0.23.3/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.1/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI57pYBNnsLAa34= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= @@ -330,6 +333,7 @@ github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 h1:etuLgGEojecsDOYTII8rYiGH github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2/go.mod h1:Zpk/LOb2sKqwP2lmHjaZT9AdaKsHPSbNLm2Uql5IQ/0= github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 h1:BtEN5Empw62/RVnZ0VcJaVtVlBijnLlJY+dwjAye2Bg= github.com/btcsuite/btcwallet/wallet/txrules v1.2.0/go.mod h1:AtkqiL7ccKWxuLYtZm8Bu8G6q82w4yIZdgq6riy60z0= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.2/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 h1:PszOub7iXVYbtGybym5TGCp9Dv1h1iX4rIC3HICZGLg= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3/go.mod h1:q08Rms52VyWyXcp5zDc4tdFRKkFgNsMQrv3/LvE1448= github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= @@ -339,6 +343,7 @@ github.com/btcsuite/btcwallet/wtxmgr v1.5.0 h1:WO0KyN4l6H3JWnlFxfGR7r3gDnlGT7W2c github.com/btcsuite/btcwallet/wtxmgr v1.5.0/go.mod h1:TQVDhFxseiGtZwEPvLgtfyxuNUDsIdaJdshvWzR0HJ4= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= @@ -1025,6 +1030,7 @@ github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5Of github.com/linxGnu/grocksdb v1.8.14/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/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -1160,8 +1166,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pebbe/zmq4 v1.2.9 h1:JlHcdgq6zpppNR1tH0wXJq0XK03pRUc4lBlHTD7aj/4= -github.com/pebbe/zmq4 v1.2.9/go.mod h1:nqnPueOapVhE2wItZ0uOErngczsJdLOGkebMxaO8r48= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= diff --git a/monitor/btcscanner/block_handler.go b/monitor/btcscanner/block_handler.go index 7054f9a4..704d1b9b 100644 --- a/monitor/btcscanner/block_handler.go +++ b/monitor/btcscanner/block_handler.go @@ -3,75 +3,90 @@ package btcscanner import ( "errors" "fmt" - - "github.com/babylonlabs-io/vigilante/types" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/chainntnfs" ) -// blockEventHandler handles connected and disconnected blocks from the BTC client. -func (bs *BtcScanner) blockEventHandler() { +// bootstrapAndBlockEventHandler handles connected and disconnected blocks from the BTC client. +func (bs *BtcScanner) bootstrapAndBlockEventHandler() { defer bs.wg.Done() + bs.Bootstrap() + + var blockEpoch *chainntnfs.BlockEpoch + bestKnownBlock := bs.UnconfirmedBlockCache.Tip() + if bestKnownBlock != nil { + hash := bestKnownBlock.BlockHash() + blockEpoch = &chainntnfs.BlockEpoch{ + Hash: &hash, + Height: bestKnownBlock.Height, + BlockHeader: bestKnownBlock.Header, + } + } + // register the notifier with the best known tip + blockNotifier, err := bs.btcNotifier.RegisterBlockEpochNtfn(blockEpoch) + if err != nil { + bs.logger.Errorf("Failed registering block epoch notifier") + return + } + defer blockNotifier.Cancel() + for { select { case <-bs.quit: bs.BtcClient.Stop() return - case event, open := <-bs.BtcClient.BlockEventChan(): + case epoch, open := <-blockNotifier.Epochs: if !open { bs.logger.Errorf("Block event channel is closed") return // channel closed } - if event.EventType == types.BlockConnected { - err := bs.handleConnectedBlocks(event) - if err != nil { - bs.logger.Warnf("failed to handle a connected block at height %d: %s, "+ - "need to restart the bootstrapping process", event.Height, err.Error()) - if bs.Synced.Swap(false) { - bs.Bootstrap() - } - } - } else if event.EventType == types.BlockDisconnected { - err := bs.handleDisconnectedBlocks(event) - if err != nil { - bs.logger.Warnf("failed to handle a disconnected block at height %d: %s,"+ - "need to restart the bootstrapping process", event.Height, err.Error()) - if bs.Synced.Swap(false) { - bs.Bootstrap() - } - } + + if err := bs.handleNewBlock(epoch.Height, epoch.BlockHeader); err != nil { + bs.logger.Warnf("failed to handle block at height %d: %s, "+ + "need to restart the bootstrapping process", epoch.Height, err.Error()) + bs.Bootstrap() } } } } -// handleConnectedBlocks handles connected blocks from the BTC client +// handleNewBlock handles blocks from the BTC client // if new confirmed blocks are found, send them through the channel -func (bs *BtcScanner) handleConnectedBlocks(event *types.BlockEvent) error { - if !bs.Synced.Load() { - return errors.New("the btc scanner is not synced") - } - - // get the block from hash - blockHash := event.Header.BlockHash() - ib, _, err := bs.BtcClient.GetBlockByHash(&blockHash) - if err != nil { - // failing to request the block, which means a bug - panic(err) - } - +func (bs *BtcScanner) handleNewBlock(height int32, header *wire.BlockHeader) error { // get cache tip cacheTip := bs.UnconfirmedBlockCache.Tip() if cacheTip == nil { return errors.New("no unconfirmed blocks found") } - parentHash := ib.Header.PrevBlock + if cacheTip.Height >= height { + bs.logger.Debugf( + "the connecting block (height: %d, hash: %s) is too early, skipping the block", + height, + header.BlockHash().String(), + ) + return nil + } + + if cacheTip.Height+1 < height { + return fmt.Errorf("missing blocks, expected block height: %d, got: %d", cacheTip.Height+1, height) + } + parentHash := header.PrevBlock // if the parent of the block is not the tip of the cache, then the cache is not up-to-date if parentHash != cacheTip.BlockHash() { return errors.New("cache is not up-to-date") } + // get the block from hash + blockHash := header.BlockHash() + ib, _, err := bs.BtcClient.GetBlockByHash(&blockHash) + if err != nil { + // failing to request the block, which means a bug + panic(fmt.Errorf("failed to request block by hash: %s", blockHash.String())) + } + // otherwise, add the block to the cache bs.UnconfirmedBlockCache.Add(ib) @@ -94,24 +109,3 @@ func (bs *BtcScanner) handleConnectedBlocks(event *types.BlockEvent) error { return nil } - -// handleDisconnectedBlocks handles disconnected blocks from the BTC client. -func (bs *BtcScanner) handleDisconnectedBlocks(event *types.BlockEvent) error { - // get cache tip - cacheTip := bs.UnconfirmedBlockCache.Tip() - if cacheTip == nil { - return errors.New("cache is empty") - } - - // if the block to be disconnected is not the tip of the cache, then the cache is not up-to-date, - if event.Header.BlockHash() != cacheTip.BlockHash() { - return errors.New("cache is out-of-sync") - } - - // otherwise, remove the block from the cache - if err := bs.UnconfirmedBlockCache.RemoveLast(); err != nil { - return fmt.Errorf("failed to remove last block from cache: %v", err) - } - - return nil -} diff --git a/monitor/btcscanner/btc_scanner.go b/monitor/btcscanner/btc_scanner.go index 5ed6ebe4..16da44ab 100644 --- a/monitor/btcscanner/btc_scanner.go +++ b/monitor/btcscanner/btc_scanner.go @@ -2,6 +2,7 @@ package btcscanner import ( "fmt" + notifier "github.com/lightningnetwork/lnd/chainntnfs" "sync" "github.com/babylonlabs-io/babylon/btctxformatter" @@ -19,7 +20,8 @@ type BtcScanner struct { logger *zap.SugaredLogger // connect to BTC node - BtcClient btcclient.BTCClient + BtcClient btcclient.BTCClient + btcNotifier notifier.ChainNotifier // the BTC height the scanner starts BaseHeight uint64 @@ -38,8 +40,6 @@ type BtcScanner struct { blockHeaderChan chan *wire.BlockHeader checkpointsChan chan *types.CheckpointRecord - Synced *atomic.Bool - wg sync.WaitGroup Started *atomic.Bool quit chan struct{} @@ -49,7 +49,8 @@ func New( monitorCfg *config.MonitorConfig, parentLogger *zap.Logger, btcClient btcclient.BTCClient, - btclightclientBaseHeight uint64, + btcNotifier notifier.ChainNotifier, + btcLightClientBaseHeight uint64, checkpointTag []byte, ) (*BtcScanner, error) { headersChan := make(chan *wire.BlockHeader, monitorCfg.BtcBlockBufferSize) @@ -64,14 +65,14 @@ func New( return &BtcScanner{ logger: parentLogger.With(zap.String("module", "btcscanner")).Sugar(), BtcClient: btcClient, - BaseHeight: btclightclientBaseHeight, + btcNotifier: btcNotifier, + BaseHeight: btcLightClientBaseHeight, K: monitorCfg.BtcConfirmationDepth, ckptCache: ckptCache, UnconfirmedBlockCache: unconfirmedBlockCache, ConfirmedBlocksChan: confirmedBlocksChan, blockHeaderChan: headersChan, checkpointsChan: ckptsChan, - Synced: atomic.NewBool(false), Started: atomic.NewBool(false), quit: make(chan struct{}), }, nil @@ -84,17 +85,17 @@ func (bs *BtcScanner) Start() { return } - // the bootstrapping should not block the main thread - go bs.Bootstrap() - - bs.BtcClient.MustSubscribeBlocks() - bs.Started.Store(true) bs.logger.Info("the BTC scanner is started") + if err := bs.btcNotifier.Start(); err != nil { + bs.logger.Errorf("Failed starting notifier") + return + } + // start handling new blocks bs.wg.Add(1) - go bs.blockEventHandler() + go bs.bootstrapAndBlockEventHandler() for bs.Started.Load() { select { @@ -128,12 +129,6 @@ func (bs *BtcScanner) Bootstrap() { err error ) - if bs.Synced.Load() { - // the scanner is already synced - return - } - defer bs.Synced.Store(true) - if bs.confirmedTipBlock != nil { firstUnconfirmedHeight = uint64(bs.confirmedTipBlock.Height + 1) } else { diff --git a/monitor/btcscanner/btc_scanner_test.go b/monitor/btcscanner/btc_scanner_test.go index 74b8f195..cb91d70d 100644 --- a/monitor/btcscanner/btc_scanner_test.go +++ b/monitor/btcscanner/btc_scanner_test.go @@ -5,15 +5,13 @@ import ( "testing" "github.com/babylonlabs-io/babylon/testutil/datagen" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "go.uber.org/atomic" - "github.com/babylonlabs-io/vigilante/config" "github.com/babylonlabs-io/vigilante/monitor/btcscanner" vdatagen "github.com/babylonlabs-io/vigilante/testutil/datagen" "github.com/babylonlabs-io/vigilante/testutil/mocks" "github.com/babylonlabs-io/vigilante/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" ) func FuzzBootStrap(f *testing.F) { @@ -30,7 +28,6 @@ func FuzzBootStrap(f *testing.F) { ctl := gomock.NewController(t) mockBtcClient := mocks.NewMockBTCClient(ctl) confirmedBlocks := chainIndexedBlocks[:numBlocks-k] - mockBtcClient.EXPECT().MustSubscribeBlocks().Return().AnyTimes() mockBtcClient.EXPECT().GetBestBlock().Return(nil, uint64(bestHeight), nil) for i := 0; i < int(numBlocks); i++ { mockBtcClient.EXPECT().GetBlockByHeight(gomock.Eq(uint64(chainIndexedBlocks[i].Height))). @@ -45,7 +42,6 @@ func FuzzBootStrap(f *testing.F) { K: k, ConfirmedBlocksChan: make(chan *types.IndexedBlock), UnconfirmedBlockCache: cache, - Synced: atomic.NewBool(false), } logger, err := config.NewRootLogger("auto", "debug") require.NoError(t, err) @@ -58,6 +54,5 @@ func FuzzBootStrap(f *testing.F) { } }() btcScanner.Bootstrap() - require.True(t, btcScanner.Synced.Load()) }) } diff --git a/monitor/monitor.go b/monitor/monitor.go index e3fd7fff..e9199256 100644 --- a/monitor/monitor.go +++ b/monitor/monitor.go @@ -3,6 +3,7 @@ package monitor import ( "encoding/hex" "fmt" + notifier "github.com/lightningnetwork/lnd/chainntnfs" "sort" "sync" @@ -52,6 +53,7 @@ func New( genesisInfo *types.GenesisInfo, bbnQueryClient BabylonQueryClient, btcClient btcclient.BTCClient, + btcNotifier notifier.ChainNotifier, monitorMetrics *metrics.MonitorMetrics, ) (*Monitor, error) { logger := parentLogger.With(zap.String("module", "monitor")) @@ -64,6 +66,7 @@ func New( cfg, logger, btcClient, + btcNotifier, genesisInfo.GetBaseBTCHeight(), checkpointTagBytes, ) diff --git a/reporter/block_handler.go b/reporter/block_handler.go index 1927d78a..f14dea32 100644 --- a/reporter/block_handler.go +++ b/reporter/block_handler.go @@ -2,35 +2,30 @@ package reporter import ( "fmt" - "github.com/babylonlabs-io/vigilante/types" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/chainntnfs" ) // blockEventHandler handles connected and disconnected blocks from the BTC client. -func (r *Reporter) blockEventHandler() { +func (r *Reporter) blockEventHandler(blockNotifier *chainntnfs.BlockEpochEvent) { defer r.wg.Done() quit := r.quitChan() + defer blockNotifier.Cancel() + for { select { - case event, open := <-r.btcClient.BlockEventChan(): + case epoch, open := <-blockNotifier.Epochs: if !open { r.logger.Errorf("Block event channel is closed") return // channel closed } - var errorRequiringBootstrap error - if event.EventType == types.BlockConnected { - errorRequiringBootstrap = r.handleConnectedBlocks(event) - } else if event.EventType == types.BlockDisconnected { - errorRequiringBootstrap = r.handleDisconnectedBlocks(event) + if err := r.handleNewBlock(epoch.Height, epoch.BlockHeader); err != nil { + r.logger.Warnf("Due to error in event processing: %v, bootstrap process need to be restarted", err) + r.bootstrapWithRetries() } - - if errorRequiringBootstrap != nil { - r.logger.Warnf("Due to error in event processing: %v, bootstrap process need to be restarted", errorRequiringBootstrap) - r.bootstrapWithRetries(true) - } - case <-quit: // We have been asked to stop return @@ -38,136 +33,67 @@ func (r *Reporter) blockEventHandler() { } } -// handleConnectedBlocks handles connected blocks from the BTC client. -func (r *Reporter) handleConnectedBlocks(event *types.BlockEvent) error { - // if the header is too early, ignore it - // NOTE: this might happen when bootstrapping is triggered after the reporter - // has subscribed to the BTC blocks - firstCacheBlock := r.btcCache.First() - if firstCacheBlock == nil { +// handleNewBlock processes a new block, checking if it connects to the cache or requires bootstrapping. +func (r *Reporter) handleNewBlock(height int32, header *wire.BlockHeader) error { + cacheTip := r.btcCache.Tip() + if cacheTip == nil { return fmt.Errorf("cache is empty, restart bootstrap process") } - if event.Height < firstCacheBlock.Height { + + if cacheTip.Height >= height { r.logger.Debugf( "the connecting block (height: %d, hash: %s) is too early, skipping the block", - event.Height, - event.Header.BlockHash().String(), + height, + header.BlockHash().String(), ) return nil } - // if the received header is within the cache's region, then this means the events have - // an overlap with the cache. Then, perform a consistency check. If the block is duplicated, - // then ignore the block, otherwise there is an inconsistency and redo bootstrap - // NOTE: this might happen when bootstrapping is triggered after the reporter - // has subscribed to the BTC blocks - if b := r.btcCache.FindBlock(uint64(event.Height)); b != nil { - if b.BlockHash() == event.Header.BlockHash() { - r.logger.Debugf( - "the connecting block (height: %d, hash: %s) is known to cache, skipping the block", - b.Height, - b.BlockHash().String(), - ) - return nil - } - return fmt.Errorf( - "the connecting block (height: %d, hash: %s) is different from the header (height: %d, hash: %s) at the same height in cache", - event.Height, - event.Header.BlockHash().String(), - b.Height, - b.BlockHash().String(), - ) + if cacheTip.Height+1 < height { + return fmt.Errorf("missing blocks, expected block height: %d, got: %d", cacheTip.Height+1, height) } - // get the block from hash - blockHash := event.Header.BlockHash() - ib, mBlock, err := r.btcClient.GetBlockByHash(&blockHash) - if err != nil { - return fmt.Errorf("failed to get block %v with number %d ,from BTC client: %w", blockHash, event.Height, err) + // Check if the new block connects to the cache cacheTip + parentHash := header.PrevBlock + if parentHash != cacheTip.BlockHash() { + // If the block doesn't connect, clear the cache and bootstrap + r.btcCache.RemoveAll() + return fmt.Errorf("block does not connect to the cache, diff hash, bootstrap required") } - // if the parent of the block is not the tip of the cache, then the cache is not up-to-date, - // and we might have missed some blocks. In this case, restart the bootstrap process. - parentHash := mBlock.Header.PrevBlock - cacheTip := r.btcCache.Tip() // NOTE: cache is guaranteed to be non-empty at this stage - if parentHash != cacheTip.BlockHash() { - return fmt.Errorf("cache (tip %d) is not up-to-date while connecting block %d, restart bootstrap process", cacheTip.Height, ib.Height) + // Block connects to the current chain, add it to the cache + blockHash := header.BlockHash() + ib, _, err := r.btcClient.GetBlockByHash(&blockHash) + if err != nil { + return fmt.Errorf("failed to get block %v with height %d: %w", blockHash, height, err) } - // otherwise, add the block to the cache r.btcCache.Add(ib) - var headersToProcess []*types.IndexedBlock - - if r.reorgList.size() > 0 { - // we are in the middle of reorg, we need to check whether we already have all blocks of better chain - // as reorgs in btc nodes happen only when better chain is available. - // 1. First we get oldest header from our reorg branch - // 2. Then we get all headers from our cache starting the height of the oldest header of new branch - // 3. then we calculate if work on new branch starting from the first reorged height is larger - // than removed branch work. - oldestBlockFromOldBranch := r.reorgList.getLastRemovedBlock() - currentBranch, err := r.btcCache.GetLastBlocks(oldestBlockFromOldBranch.height) - if err != nil { - panic(fmt.Errorf("failed to get block from cache after reorg: %w", err)) - } - - currentBranchWork := calculateBranchWork(currentBranch) + // Process the new block (submit headers, checkpoints, etc.) + return r.processNewBlock(ib) +} - // if current branch is better than reorg branch, we can submit headers and clear reorg list - if currentBranchWork.GT(r.reorgList.removedBranchWork()) { - r.logger.Debugf("Current branch is better than reorg branch. Length of current branch: %d, work of branch: %s", len(currentBranch), currentBranchWork) - headersToProcess = append(headersToProcess, currentBranch...) - r.reorgList.clear() - } - } else { - headersToProcess = append(headersToProcess, ib) - } +// processNewBlock handles further processing of a newly added block. +func (r *Reporter) processNewBlock(ib *types.IndexedBlock) error { + var headersToProcess []*types.IndexedBlock + headersToProcess = append(headersToProcess, ib) if len(headersToProcess) == 0 { r.logger.Debug("No new headers to submit to Babylon") return nil } - // extracts and submits headers for each blocks in ibs signer := r.babylonClient.MustGetAddr() - _, err = r.ProcessHeaders(signer, headersToProcess) - if err != nil { - r.logger.Warnf("Failed to submit header: %v", err) - } - - // extracts and submits checkpoints for each blocks in ibs - _, _, err = r.ProcessCheckpoints(signer, headersToProcess) - if err != nil { - r.logger.Warnf("Failed to submit checkpoint: %v", err) - } - return nil -} - -// handleDisconnectedBlocks handles disconnected blocks from the BTC client. -func (r *Reporter) handleDisconnectedBlocks(event *types.BlockEvent) error { - // get cache tip - cacheTip := r.btcCache.Tip() - if cacheTip == nil { - return fmt.Errorf("cache is empty, restart bootstrap process") - } - // if the block to be disconnected is not the tip of the cache, then the cache is not up-to-date, - if event.Header.BlockHash() != cacheTip.BlockHash() { - return fmt.Errorf("cache is not up-to-date while disconnecting block, restart bootstrap process") + // Process headers + if _, err := r.ProcessHeaders(signer, headersToProcess); err != nil { + r.logger.Warnf("Failed to submit headers: %v", err) } - // at this point, the block to be disconnected is the tip of the cache so we can - // add it to our reorg list - r.reorgList.addRemovedBlock( - uint64(cacheTip.Height), - cacheTip.Header, - ) - - // otherwise, remove the block from the cache - if err := r.btcCache.RemoveLast(); err != nil { - r.logger.Warnf("Failed to remove last block from cache: %v, restart bootstrap process", err) - panic(err) + // Process checkpoints + if _, _, err := r.ProcessCheckpoints(signer, headersToProcess); err != nil { + r.logger.Warnf("Failed to submit checkpoints: %v", err) } return nil diff --git a/reporter/bootstrapping.go b/reporter/bootstrapping.go index 5e45c678..729fb1eb 100644 --- a/reporter/bootstrapping.go +++ b/reporter/bootstrapping.go @@ -60,16 +60,13 @@ func (r *Reporter) checkConsistency() (*consistencyCheckInfo, error) { }, nil } -func (r *Reporter) bootstrap(skipBlockSubscription bool) error { +func (r *Reporter) bootstrap() error { var ( btcLatestBlockHeight uint64 ibs []*types.IndexedBlock err error ) - // if we are bootstraping, we will definitely not handle reorgs - r.reorgList.clear() - // ensure BTC has caught up with BBN header chain if err := r.waitUntilBTCSync(); err != nil { return err @@ -81,14 +78,7 @@ func (r *Reporter) bootstrap(skipBlockSubscription bool) error { } r.logger.Debugf("BTC cache size: %d", r.btcCache.Size()) - // Subscribe new blocks right after initialising BTC cache, in order to ensure subscribed blocks and cached blocks do not have overlap. - // Otherwise, if we subscribe too early, then they will have overlap, leading to duplicated header/ckpt submissions. - if !skipBlockSubscription { - r.btcClient.MustSubscribeBlocks() - } - consistencyInfo, err := r.checkConsistency() - if err != nil { return err } @@ -132,6 +122,7 @@ func (r *Reporter) bootstrap(skipBlockSubscription bool) error { } r.logger.Info("Successfully finished bootstrapping") + return nil } @@ -153,12 +144,12 @@ func (r *Reporter) reporterQuitCtx() (context.Context, func()) { return ctx, cancel } -func (r *Reporter) bootstrapWithRetries(skipBlockSubscription bool) { +func (r *Reporter) bootstrapWithRetries() { // if we are exiting, we need to cancel this process ctx, cancel := r.reporterQuitCtx() defer cancel() if err := retry.Do(func() error { - return r.bootstrap(skipBlockSubscription) + return r.bootstrap() }, retry.Context(ctx), bootstrapAttemptsAtt, @@ -226,6 +217,7 @@ func (r *Reporter) initBTCCache() error { if err = r.btcCache.Init(ibs); err != nil { panic(err) } + return nil } @@ -316,5 +308,6 @@ func (r *Reporter) checkHeaderConsistency(consistencyCheckHeight uint64) error { err = fmt.Errorf("BTC main chain is inconsistent with BBN header chain: k-deep block in BBN header chain: %v", consistencyCheckHash) panic(err) } + return nil } diff --git a/reporter/reorg_list.go b/reporter/reorg_list.go deleted file mode 100644 index 6e24c938..00000000 --- a/reporter/reorg_list.go +++ /dev/null @@ -1,76 +0,0 @@ -package reporter - -import ( - "sync" - - sdkmath "cosmossdk.io/math" - btclightclienttypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" - "github.com/btcsuite/btcd/wire" -) - -type removedBlock struct { - height uint64 - header *wire.BlockHeader -} - -// Help data structure to keep track of removed blocks. -// NOTE: This is not generic data structure, and must be used with conjunction with -// reporter and btc cache -type reorgList struct { - sync.Mutex - workOfRemovedBlocks sdkmath.Uint - removedBlocks []*removedBlock -} - -func newReorgList() *reorgList { - return &reorgList{ - removedBlocks: []*removedBlock{}, - workOfRemovedBlocks: sdkmath.ZeroUint(), - } -} - -// addRemovedBlock add currently removed block to the end of the list. Re-orgs -// are started from the tip of the chain and go backwards, this means -// that oldest removed block is at the end of the list. -func (r *reorgList) addRemovedBlock( - height uint64, - header *wire.BlockHeader) { - headerWork := btclightclienttypes.CalcHeaderWork(header) - r.Lock() - defer r.Unlock() - - newWork := btclightclienttypes.CumulativeWork(headerWork, r.workOfRemovedBlocks) - r.removedBlocks = append(r.removedBlocks, &removedBlock{height, header}) - r.workOfRemovedBlocks = newWork -} - -func (r *reorgList) getLastRemovedBlock() *removedBlock { - r.Lock() - defer r.Unlock() - if len(r.removedBlocks) == 0 { - return nil - } - - return r.removedBlocks[len(r.removedBlocks)-1] -} - -func (r *reorgList) clear() { - r.Lock() - defer r.Unlock() - - r.removedBlocks = []*removedBlock{} - r.workOfRemovedBlocks = sdkmath.ZeroUint() -} - -func (r *reorgList) size() int { - r.Lock() - defer r.Unlock() - - return len(r.removedBlocks) -} - -func (r *reorgList) removedBranchWork() sdkmath.Uint { - r.Lock() - defer r.Unlock() - return r.workOfRemovedBlocks -} diff --git a/reporter/reporter.go b/reporter/reporter.go index 5a5bfb64..5c2f757c 100644 --- a/reporter/reporter.go +++ b/reporter/reporter.go @@ -3,6 +3,7 @@ package reporter import ( "encoding/hex" "fmt" + notifier "github.com/lightningnetwork/lnd/chainntnfs" "sync" "time" @@ -22,6 +23,7 @@ type Reporter struct { btcClient btcclient.BTCClient babylonClient BabylonClient + btcNotifier notifier.ChainNotifier // retry attributes retrySleepTime time.Duration @@ -30,7 +32,6 @@ type Reporter struct { // Internal states of the reporter CheckpointCache *types.CheckpointCache btcCache *types.BTCCache - reorgList *reorgList btcConfirmationDepth uint64 checkpointFinalizationTimeout uint64 metrics *metrics.ReporterMetrics @@ -45,6 +46,7 @@ func New( parentLogger *zap.Logger, btcClient btcclient.BTCClient, babylonClient BabylonClient, + btcNotifier notifier.ChainNotifier, retrySleepTime, maxRetrySleepTime time.Duration, metrics *metrics.ReporterMetrics, @@ -81,8 +83,8 @@ func New( maxRetrySleepTime: maxRetrySleepTime, btcClient: btcClient, babylonClient: babylonClient, + btcNotifier: btcNotifier, CheckpointCache: ckptCache, - reorgList: newReorgList(), btcConfirmationDepth: k, checkpointFinalizationTimeout: w, metrics: metrics, @@ -108,10 +110,21 @@ func (r *Reporter) Start() { } r.quitMu.Unlock() - r.bootstrapWithRetries(false) + r.bootstrapWithRetries() + + if err := r.btcNotifier.Start(); err != nil { + r.logger.Errorf("Failed starting notifier") + return + } + + blockNotifier, err := r.btcNotifier.RegisterBlockEpochNtfn(nil) + if err != nil { + r.logger.Errorf("Failed registering block epoch notifier") + return + } r.wg.Add(1) - go r.blockEventHandler() + go r.blockEventHandler(blockNotifier) // start record time-related metrics r.metrics.RecordMetrics() diff --git a/reporter/utils.go b/reporter/utils.go index 59dae461..ce1ef528 100644 --- a/reporter/utils.go +++ b/reporter/utils.go @@ -7,7 +7,6 @@ import ( pv "github.com/cosmos/relayer/v2/relayer/provider" - sdkmath "cosmossdk.io/math" "github.com/babylonlabs-io/babylon/types/retry" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" @@ -224,15 +223,6 @@ func (r *Reporter) ProcessCheckpoints(signer string, ibs []*types.IndexedBlock) return numCkptSegs, numMatchedCkpts, err } -func calculateBranchWork(branch []*types.IndexedBlock) sdkmath.Uint { - var currenWork = sdkmath.ZeroUint() - for _, h := range branch { - headerWork := btclctypes.CalcHeaderWork(h.Header) - currenWork = btclctypes.CumulativeWork(headerWork, currenWork) - } - return currenWork -} - // push msg to channel c, or quit if quit channel is closed func PushOrQuit[T any](c chan<- T, msg T, quit <-chan struct{}) { select { diff --git a/reporter/utils_test.go b/reporter/utils_test.go index d575c5ff..ab32234e 100644 --- a/reporter/utils_test.go +++ b/reporter/utils_test.go @@ -1,6 +1,7 @@ package reporter_test import ( + "github.com/lightningnetwork/lnd/lntest/mock" "math/rand" "testing" @@ -32,12 +33,14 @@ func newMockReporter(t *testing.T, ctrl *gomock.Controller) ( mockBabylonClient.EXPECT().GetConfig().Return(&cfg.Babylon).AnyTimes() mockBabylonClient.EXPECT().BTCCheckpointParams().Return( &btcctypes.QueryParamsResponse{Params: btccParams}, nil).AnyTimes() + mockNotifier := mock.ChainNotifier{} r, err := reporter.New( &cfg.Reporter, logger, mockBTCClient, mockBabylonClient, + &mockNotifier, cfg.Common.RetrySleepTime, cfg.Common.MaxRetrySleepTime, metrics.NewReporterMetrics(), diff --git a/testutil/mocks/btcclient.go b/testutil/mocks/btcclient.go index 1bbdb6b9..76581b6c 100644 --- a/testutil/mocks/btcclient.go +++ b/testutil/mocks/btcclient.go @@ -40,20 +40,6 @@ func (m *MockBTCClient) EXPECT() *MockBTCClientMockRecorder { return m.recorder } -// BlockEventChan mocks base method. -func (m *MockBTCClient) BlockEventChan() <-chan *types.BlockEvent { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BlockEventChan") - ret0, _ := ret[0].(<-chan *types.BlockEvent) - return ret0 -} - -// BlockEventChan indicates an expected call of BlockEventChan. -func (mr *MockBTCClientMockRecorder) BlockEventChan() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockEventChan", reflect.TypeOf((*MockBTCClient)(nil).BlockEventChan)) -} - // FindTailBlocksByHeight mocks base method. func (m *MockBTCClient) FindTailBlocksByHeight(height uint64) ([]*types.IndexedBlock, error) { m.ctrl.T.Helper() @@ -162,18 +148,6 @@ func (mr *MockBTCClientMockRecorder) GetTxOut(txHash, index, mempool interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTxOut", reflect.TypeOf((*MockBTCClient)(nil).GetTxOut), txHash, index, mempool) } -// MustSubscribeBlocks mocks base method. -func (m *MockBTCClient) MustSubscribeBlocks() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "MustSubscribeBlocks") -} - -// MustSubscribeBlocks indicates an expected call of MustSubscribeBlocks. -func (mr *MockBTCClientMockRecorder) MustSubscribeBlocks() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MustSubscribeBlocks", reflect.TypeOf((*MockBTCClient)(nil).MustSubscribeBlocks)) -} - // SendRawTransaction mocks base method. func (m *MockBTCClient) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) { m.ctrl.T.Helper() @@ -236,21 +210,6 @@ func (m *MockBTCWallet) EXPECT() *MockBTCWalletMockRecorder { return m.recorder } -// DumpPrivKey mocks base method. -func (m *MockBTCWallet) DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DumpPrivKey", address) - ret0, _ := ret[0].(*btcutil.WIF) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DumpPrivKey indicates an expected call of DumpPrivKey. -func (mr *MockBTCWalletMockRecorder) DumpPrivKey(address interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DumpPrivKey", reflect.TypeOf((*MockBTCWallet)(nil).DumpPrivKey), address) -} - // FundRawTransaction mocks base method. func (m *MockBTCWallet) FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) { m.ctrl.T.Helper() diff --git a/zmq/client.go b/zmq/client.go deleted file mode 100644 index 51184f42..00000000 --- a/zmq/client.go +++ /dev/null @@ -1,129 +0,0 @@ -// Package zmq reference is taken from https://github.com/joakimofv/go-bitcoindclient which is a -// go wrapper around official zmq package https://github.com/pebbe/zmq4 -package zmq - -import ( - "errors" - "sync" - "sync/atomic" - - "github.com/babylonlabs-io/vigilante/types" - "github.com/btcsuite/btcd/rpcclient" - "github.com/pebbe/zmq4" - "go.uber.org/zap" -) - -var ( - ErrSubscribeDisabled = errors.New("subscribe disabled (ZmqEndpoint was not set)") - ErrSubscribeExited = errors.New("subscription backend has exited") - ErrSubscriptionAlreadyActive = errors.New("active subscription already exists") -) - -// Client is a client that provides methods for interacting with zmq4. -// Must be created with New and destroyed with Close. -// Clients are safe for concurrent use by multiple goroutines. -type Client struct { - rpcClient *rpcclient.Client - logger *zap.SugaredLogger - closed int32 // Set atomically. - wg sync.WaitGroup - quit chan struct{} - - zmqEndpoint string - blockEventChan chan *types.BlockEvent - - // ZMQ subscription related things. - zctx *zmq4.Context - zsub *zmq4.Socket - subs subscriptions - // subs.zfront --> zback is used like a channel to send messages to the zmqHandler goroutine. - // Have to use zmq4 sockets in place of native channels for communication from - // other functions to the goroutine, since it is constantly waiting on the zsub socket, - // it can't select on a channel at the same time but can poll on multiple sockets. - zback *zmq4.Socket -} - -// New returns an initiated client, or an error. -func New(parentLogger *zap.Logger, zmqEndpoint string, blockEventChan chan *types.BlockEvent, rpcClient *rpcclient.Client) (*Client, error) { - var ( - zctx *zmq4.Context - zsub *zmq4.Socket - zback *zmq4.Socket - err error - c = &Client{ - quit: make(chan struct{}), - rpcClient: rpcClient, - zmqEndpoint: zmqEndpoint, - logger: parentLogger.With(zap.String("module", "zmq")).Sugar(), - } - ) - - // ZMQ Subscribe. - zctx, err = zmq4.NewContext() - if err != nil { - return nil, err - } - - zsub, err = zctx.NewSocket(zmq4.SUB) - if err != nil { - return nil, err - } - if err = zsub.Connect(zmqEndpoint); err != nil { - return nil, err - } - - zback, err = zctx.NewSocket(zmq4.PAIR) - if err != nil { - return nil, err - } - if err = zback.Bind("inproc://channel"); err != nil { - return nil, err - } - - zfront, err := zctx.NewSocket(zmq4.PAIR) - if err != nil { - return nil, err - } - if err = zfront.Connect("inproc://channel"); err != nil { - return nil, err - } - - c.zctx = zctx - c.zsub = zsub - c.subs.exited = make(chan struct{}) - c.subs.zfront = zfront - c.zback = zback - c.blockEventChan = blockEventChan - - c.wg.Add(1) - go c.zmqHandler() - - return c, nil -} - -// Close terminates the client and releases resources. -func (c *Client) Close() (err error) { - if !atomic.CompareAndSwapInt32(&c.closed, 0, 1) { - return errors.New("client already closed") - } - if c.zctx != nil { - c.zctx.SetRetryAfterEINTR(false) - c.subs.Lock() - select { - case <-c.subs.exited: - default: - if _, err = c.subs.zfront.SendMessage("term"); err != nil { - return err - } - } - c.subs.Unlock() - <-c.subs.exited - err = c.zctx.Term() - if err != nil { - return err - } - } - close(c.quit) - c.wg.Wait() - return nil -} diff --git a/zmq/subscribe.go b/zmq/subscribe.go deleted file mode 100644 index 2df13ba6..00000000 --- a/zmq/subscribe.go +++ /dev/null @@ -1,182 +0,0 @@ -package zmq - -import ( - "encoding/hex" - "sync" - "time" - - "github.com/babylonlabs-io/vigilante/types" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - zmq "github.com/pebbe/zmq4" -) - -// SequenceMsg is a subscription event coming from a "sequence" ZMQ message. -type SequenceMsg struct { - Hash [32]byte // use encoding/hex.EncodeToString() to get it into the RPC method string format. - Event types.EventType -} - -type subscriptions struct { - sync.RWMutex - - exited chan struct{} - zfront *zmq.Socket - latestEvent time.Time - active bool -} - -// SubscribeSequence subscribes to the ZMQ "sequence" messages as SequenceMsg items pushed onto the channel. -// Call cancel to cancel the subscription and let the client release the resources. The channel is closed -// when the subscription is canceled or when the client is closed. -func (c *Client) SubscribeSequence() (err error) { - if c.zsub == nil { - err = ErrSubscribeDisabled - return - } - c.subs.Lock() - select { - case <-c.subs.exited: - err = ErrSubscribeExited - c.subs.Unlock() - return - default: - } - - if c.subs.active { - err = ErrSubscriptionAlreadyActive - return - } - - _, err = c.subs.zfront.SendMessage("subscribe", "sequence") - if err != nil { - c.subs.Unlock() - return - } - c.subs.active = true - - c.subs.Unlock() - return -} - -func (c *Client) zmqHandler() { - defer c.wg.Done() - defer func(zsub *zmq.Socket) { - err := zsub.Close() - if err != nil { - c.logger.Errorf("Error closing ZMQ socket: %v", err) - } - }(c.zsub) - defer func(zback *zmq.Socket) { - err := zback.Close() - if err != nil { - c.logger.Errorf("Error closing ZMQ socket: %v", err) - } - }(c.zback) - - poller := zmq.NewPoller() - poller.Add(c.zsub, zmq.POLLIN) - poller.Add(c.zback, zmq.POLLIN) -OUTER: - for { - // Wait forever until a message can be received or the context was cancelled. - polled, err := poller.Poll(-1) - if err != nil { - break OUTER - } - - for _, p := range polled { - switch p.Socket { - case c.zsub: - msg, err := c.zsub.RecvMessage(0) - if err != nil { - break OUTER - } - c.subs.latestEvent = time.Now() - switch msg[0] { - case "sequence": - var sequenceMsg SequenceMsg - copy(sequenceMsg.Hash[:], msg[1]) - switch msg[1][32] { - case 'C': - sequenceMsg.Event = types.BlockConnected - case 'D': - sequenceMsg.Event = types.BlockDisconnected - default: - // not interested in other events - continue - } - - c.sendBlockEvent(sequenceMsg.Hash[:], sequenceMsg.Event) - } - - case c.zback: - msg, err := c.zback.RecvMessage(0) - if err != nil { - break OUTER - } - switch msg[0] { - case "subscribe": - if err := c.zsub.SetSubscribe(msg[1]); err != nil { - break OUTER - } - case "term": - break OUTER - } - } - } - } - - c.subs.Lock() - close(c.subs.exited) - err := c.subs.zfront.Close() - if err != nil { - c.logger.Errorf("Error closing zfront: %v", err) - return - } - // Close all subscriber channels. - if c.subs.active { - err = c.zsub.SetUnsubscribe("sequence") - if err != nil { - c.logger.Errorf("Error unsubscribing from sequence: %v", err) - return - } - } - - c.subs.Unlock() -} - -func (c *Client) sendBlockEvent(hash []byte, event types.EventType) { - blockHashStr := hex.EncodeToString(hash[:]) - blockHash, err := chainhash.NewHashFromStr(blockHashStr) - if err != nil { - c.logger.Errorf("Failed to parse block hash %v: %v", blockHashStr, err) - panic(err) - } - - c.logger.Infof("Received zmq sequence message for block %v", blockHashStr) - - ib, _, err := c.getBlockByHash(blockHash) - if err != nil { - c.logger.Errorf("Failed to get block %v from BTC client: %v", blockHash, err) - panic(err) - } - - c.blockEventChan <- types.NewBlockEvent(event, ib.Height, ib.Header) -} - -func (c *Client) getBlockByHash(blockHash *chainhash.Hash) (*types.IndexedBlock, *wire.MsgBlock, error) { - // TODO: ZMQ should not use BTC/RPC client, modify BlockEvent to include block hash - blockInfo, err := c.rpcClient.GetBlockVerbose(blockHash) - if err != nil { - return nil, nil, err - } - - mBlock, err := c.rpcClient.GetBlock(blockHash) - if err != nil { - return nil, nil, err - } - - btcTxs := types.GetWrappedTxs(mBlock) - return types.NewIndexedBlock(int32(blockInfo.Height), &mBlock.Header, btcTxs), mBlock, nil -} From 874630c7e8c3ae605ef14d34a43a346cf2d32ee8 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:47:53 +0200 Subject: [PATCH 09/12] chore(*): remove btcd support (#27) This PR aims to remove support for `btcd `we will be only using `bitcoind` as our backend. References [issue](https://github.com/babylonlabs-io/vigilante/issues/24). --- README.md | 113 +++++--------- btcclient/client_wallet.go | 36 ++--- btcclient/notifier.go | 197 ++++++------------------ cmd/vigilante/cmd/btcstaking_tracker.go | 2 +- cmd/vigilante/cmd/monitor.go | 2 +- cmd/vigilante/cmd/reporter.go | 2 +- config/bitcoin.go | 104 ++++--------- config/config.go | 10 +- e2etest/atomicslasher_e2e_test.go | 8 +- e2etest/reporter_e2e_test.go | 6 +- e2etest/slasher_e2e_test.go | 17 +- e2etest/test_manager.go | 5 - e2etest/unbondingwatcher_e2e_test.go | 4 +- sample-vigilante.yml | 1 - submitter/relayer/estimator.go | 68 +++----- submitter/submitter.go | 2 +- types/utils.go | 19 +-- 17 files changed, 167 insertions(+), 429 deletions(-) diff --git a/README.md b/README.md index d2f369e9..92d81b1a 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ There are four vigilante programs: ## Requirements -- Go 1.23 +- [Go 1.23](https://go.dev/dl/go1.23.0.src.tar.gz) +- [Bitcoind](https://bitcoincore.org/bin) - Package [libzmq](https://github.com/zeromq/libzmq) -- [btcd](https://github.com/btcsuite/btcd/tree/master?tab=readme-ov-file#installation) binaries (only for testing) ## Building @@ -66,100 +66,69 @@ This will be later used to retrieve the certificate required for RPC connections mkdir $TESTNET_PATH/bitcoin ``` -For a Docker deployment, we want the vigilante to be able to communicate with -the Babylon and Bitcoin instances running on the local network. -We can accomplish that through the `host.docker.internal` DNS name, -which the Docker network translates to the Docker machine. -To enable Bitcoin RPC requests, we need to add the `host.docker.internal` -DNS host to the `rpc.cert` file that was created by the previous command. -To do that we use the btcd `gencerts` utility, +```bash +# Download Bitcoin Core binary +wget https://bitcoincore.org/bin/bitcoin-core-27.0/bitcoin-27.0-x86_64-linux-gnu.tar.gz # or choose a version depending on your os -```shell -gencerts -d $TESTNET_PATH/bitcoin/ -H host.docker.internal -``` - -#### Running a Bitcoin simnet with an arbitrary mining address +# Extract the downloaded archive +tar -xvf bitcoin-27.0-x86_64-linux-gnu.tar.gz -Launch a simnet Bitcoin node -which listens for RPC connections at port `18556` and -stores the RPC certificate under the `$TESTNET_PATH/bitcoin` directory. -The mining address is arbitrary. - -```shell -btcd --simnet --rpclisten 127.0.0.1:18556 --rpcuser rpcuser --rpcpass rpcpass \ - --rpccert $TESTNET_PATH/bitcoin/rpc.cert --rpckey $TESTNET_PATH/bitcoin/rpc.key \ - --miningaddr SQqHYFTSPh8WAyJvzbAC8hoLbF12UVsE5s +# Provide execution permissions to binaries +chmod +x bitcoin-27.0/bin/bitcoind +chmod +x bitcoin-27.0/bin/bitcoin-cli ``` -#### Running a Bitcoin simnet with a wallet +#### Running a Bitcoin regtest with a wallet -Launch a simnet Bitcoin node -which listens for RPC connections at port `18556` and -stores the RPC certificate under the `$TESTNET_PATH/bitcoin` directory. +Launch a regtest Bitcoind node which listens for RPC connections at port `18443`. ```shell -btcd --simnet --rpclisten 127.0.0.1:18556 --rpcuser rpcuser --rpcpass rpcpass \ - --rpccert $TESTNET_PATH/bitcoin/rpc.cert --rpckey $TESTNET_PATH/bitcoin/rpc.key +bitcoind -regtest \ + -txindex \ + -rpcuser= \ + -rpcpassword= \ + -rpcbind=0.0.0.0:18443 \ + -zmqpubsequence=tcp://0.0.0.0:28333 \ + -datadir=/data/.bitcoin \ + ``` Leave this process running. -Then, create a simnet Bitcoin wallet. +Then, create a regtest Bitcoin wallet. If you want to use the default vigilante file, then give the password `walletpass`. Otherwise, make sure to edit the `vigilante.yaml` to reflect the correct password. ```shell -btcwallet --simnet -u rpcuser -P rpcpass \ - --rpccert $TESTNET_PATH/bitcoin/rpc-wallet.cert --rpckey $TESTNET_PATH/bitcoin/rpc-wallet.key \ - --cafile $TESTNET_PATH/bitcoin/rpc.cert \ - --create +bitcoin-cli -regtest \ + -rpcuser= \ + -rpcpassword= \ + -named createwallet \ + wallet_name="" \ + passphrase="" \ + load_on_startup=true \ + descriptors=true ``` +You can generate a btc address through the `getnewaddress` command: -The above instruction is going to prompt you for a password and going to give you the seed. -Store those securely. - -Afterwards, start the wallet service listening to port `18554`: - -```shell -btcwallet --simnet -u rpcuser -P rpcpass --rpclisten=127.0.0.1:18554 \ - --rpccert $TESTNET_PATH/bitcoin/rpc-wallet.cert --rpckey $TESTNET_PATH/bitcoin/rpc-wallet.key \ - --cafile $TESTNET_PATH/bitcoin/rpc.cert +```bash +bitcoin-cli -regtest \ + -rpcuser= \ + -rpcpassword= \ + getnewaddress ``` -Leave this process running. If you get an error that a wallet already exists and you still want -to create one, delete the `wallet.db` file located in the path displayed by the error message. - -Create an address that will be later used for mining. The output below is a sample one. - -```shell -$ btcctl --simnet --wallet -u rpcuser -P rpcpass \ - --rpccert $TESTNET_PATH/bitcoin/rpc-wallet.cert \ - --rpcserver 127.0.0.1 getnewaddress - -SQqHYFTSPh8WAyJvzbAC8hoLbF12UVsE5s -``` - -Finally, restart the btcd service with the new address. -First, kill the `btcd` process that you started in the first step, and then: - -```shell -btcd --simnet --rpclisten 127.0.0.1:18556 --rpcuser rpcuser --rpcpass rpcpass \ - --rpccert $TESTNET_PATH/bitcoin/rpc.cert --rpckey $TESTNET_PATH/bitcoin/rpc.key \ - --miningaddr $MINING_ADDRESS -``` - -where `$MINING_ADDRESS` is the address that you got as an output in the previous command. - #### Generating BTC blocks While running this setup, one might want to generate BTC blocks. -We accomplish that through the btcd `btcctl` utility and the use +We accomplish that through the `bitcoin-cli` utility and the use of the parameters we defined above. ```shell -btcctl --simnet --wallet --rpcuser=rpcuser --rpcpass=rpcpass \ - --rpccert=$TESTNET_PATH/bitcoin/rpc-wallet.cert \ - generate $NUM_BLOCKS +bitcoin-cli -chain=regtest \ + -rpcuser= \ + -rpcpassword= \ + -generate $NUM_BLOCKS ``` where `$NUM_BLOCKS` is the number of blocks you want to generate. @@ -241,7 +210,7 @@ cp sample-vigilante-docker.yml $TESTNET_PATH/vigilante/vigilante.yml make reporter-build ``` -Afterwards, run the above image and attach the directories +Afterward, run the above image and attach the directories that contain the configuration for Babylon, Bitcoin, and the vigilante. ```shell diff --git a/btcclient/client_wallet.go b/btcclient/client_wallet.go index 16c964bd..fef4835d 100644 --- a/btcclient/client_wallet.go +++ b/btcclient/client_wallet.go @@ -13,7 +13,6 @@ import ( "github.com/babylonlabs-io/vigilante/config" "github.com/babylonlabs-io/vigilante/netparams" - "github.com/babylonlabs-io/vigilante/types" ) // NewWallet creates a new BTC wallet @@ -30,38 +29,21 @@ func NewWallet(cfg *config.BTCConfig, parentLogger *zap.Logger) (*Client, error) wallet.params = params wallet.logger = parentLogger.With(zap.String("module", "btcclient_wallet")).Sugar() - connCfg := &rpcclient.ConnConfig{} - switch cfg.BtcBackend { - case types.Bitcoind: - // TODO Currently we are not using Params field of rpcclient.ConnConfig due to bug in btcd - // when handling signet. - connCfg = &rpcclient.ConnConfig{ - // this will work with node loaded with multiple wallets - Host: cfg.Endpoint + "/wallet/" + cfg.WalletName, - HTTPPostMode: true, - User: cfg.Username, - Pass: cfg.Password, - DisableTLS: cfg.DisableClientTLS, - } - case types.Btcd: - // TODO Currently we are not using Params field of rpcclient.ConnConfig due to bug in btcd - // when handling signet. - connCfg = &rpcclient.ConnConfig{ - Host: cfg.WalletEndpoint, - Endpoint: "ws", // websocket - User: cfg.Username, - Pass: cfg.Password, - DisableTLS: cfg.DisableClientTLS, - Certificates: cfg.ReadWalletCAFile(), - } + connCfg := &rpcclient.ConnConfig{ + // this will work with node loaded with multiple wallets + Host: cfg.Endpoint + "/wallet/" + cfg.WalletName, + HTTPPostMode: true, + User: cfg.Username, + Pass: cfg.Password, + DisableTLS: true, } rpcClient, err := rpcclient.New(connCfg, nil) if err != nil { - return nil, fmt.Errorf("failed to create rpc client to BTC for %s backend: %w", cfg.BtcBackend, err) + return nil, fmt.Errorf("failed to create rpc client to BTC: %w", err) } - wallet.logger.Infof("Successfully connected to %s backend", cfg.BtcBackend) + wallet.logger.Infof("Successfully connected to bitcoind") wallet.Client = rpcClient diff --git a/btcclient/notifier.go b/btcclient/notifier.go index 2ecc10ca..79f328b0 100644 --- a/btcclient/notifier.go +++ b/btcclient/notifier.go @@ -1,34 +1,19 @@ package btcclient import ( - "encoding/hex" "fmt" "github.com/babylonlabs-io/vigilante/netparams" "net" - "os" "time" "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/types" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcwallet/chain" "github.com/lightningnetwork/lnd/blockcache" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify" - "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" ) -type Btcd struct { - RPCHost string - RPCUser string - RPCPass string - RPCCert string - RawCert string - DisableTLS bool - BlockCacheSize uint64 -} - type Bitcoind struct { RPCHost string RPCUser string @@ -60,45 +45,17 @@ func DefaultBitcoindConfig() Bitcoind { } } -type BtcNodeBackendConfig struct { - Btcd *Btcd - Bitcoind *Bitcoind - ActiveNodeBackend types.SupportedBtcBackend -} - -func CfgToBtcNodeBackendConfig(cfg config.BTCConfig, rawCert string) *BtcNodeBackendConfig { - switch cfg.BtcBackend { - case types.Bitcoind: - defaultBitcoindCfg := DefaultBitcoindConfig() - // Re-rewrite defaults by values from global cfg - defaultBitcoindCfg.RPCHost = cfg.Endpoint - defaultBitcoindCfg.RPCUser = cfg.Username - defaultBitcoindCfg.RPCPass = cfg.Password - defaultBitcoindCfg.ZMQPubRawBlock = cfg.ZmqBlockEndpoint - defaultBitcoindCfg.ZMQPubRawTx = cfg.ZmqTxEndpoint - defaultBitcoindCfg.EstimateMode = cfg.EstimateMode +func ToBitcoindConfig(cfg config.BTCConfig) *Bitcoind { + defaultBitcoindCfg := DefaultBitcoindConfig() + // Re-rewrite defaults by values from global cfg + defaultBitcoindCfg.RPCHost = cfg.Endpoint + defaultBitcoindCfg.RPCUser = cfg.Username + defaultBitcoindCfg.RPCPass = cfg.Password + defaultBitcoindCfg.ZMQPubRawBlock = cfg.ZmqBlockEndpoint + defaultBitcoindCfg.ZMQPubRawTx = cfg.ZmqTxEndpoint + defaultBitcoindCfg.EstimateMode = cfg.EstimateMode - return &BtcNodeBackendConfig{ - ActiveNodeBackend: types.Bitcoind, - Bitcoind: &defaultBitcoindCfg, - } - case types.Btcd: - return &BtcNodeBackendConfig{ - ActiveNodeBackend: types.Btcd, - Btcd: &Btcd{ - RPCHost: cfg.Endpoint, - RPCUser: cfg.Username, - RPCPass: cfg.Password, - RPCCert: cfg.CAFile, - RawCert: rawCert, - DisableTLS: cfg.DisableClientTLS, - // TODO: Make block cache size configurable. Note: this is value is in bytes. - BlockCacheSize: config.DefaultBtcblockCacheSize, - }, - } - default: - panic(fmt.Sprintf("unknown btc backend: %v", cfg.BtcBackend)) - } + return &defaultBitcoindCfg } type NodeBackend struct { @@ -110,9 +67,6 @@ type HintCache interface { chainntnfs.ConfirmHintCache } -// type for disabled hint cache -// TODO: Determine if we need hint cache backed up by database which is provided -// by lnd. type EmptyHintCache struct{} var _ HintCache = (*EmptyHintCache)(nil) @@ -148,116 +102,63 @@ func BuildDialer(rpcHost string) func(string) (net.Conn, error) { } func NewNodeBackend( - cfg *BtcNodeBackendConfig, + cfg *Bitcoind, params *chaincfg.Params, hintCache HintCache, ) (*NodeBackend, error) { - switch cfg.ActiveNodeBackend { - case types.Bitcoind: - bitcoindCfg := &chain.BitcoindConfig{ - ChainParams: params, - Host: cfg.Bitcoind.RPCHost, - User: cfg.Bitcoind.RPCUser, - Pass: cfg.Bitcoind.RPCPass, - Dialer: BuildDialer(cfg.Bitcoind.RPCHost), - PrunedModeMaxPeers: cfg.Bitcoind.PrunedNodeMaxPeers, - } - - if cfg.Bitcoind.RPCPolling { - bitcoindCfg.PollingConfig = &chain.PollingConfig{ - BlockPollingInterval: cfg.Bitcoind.BlockPollingInterval, - TxPollingInterval: cfg.Bitcoind.TxPollingInterval, - TxPollingIntervalJitter: config.DefaultTxPollingJitter, - } - } else { - bitcoindCfg.ZMQConfig = &chain.ZMQConfig{ - ZMQBlockHost: cfg.Bitcoind.ZMQPubRawBlock, - ZMQTxHost: cfg.Bitcoind.ZMQPubRawTx, - ZMQReadDeadline: cfg.Bitcoind.ZMQReadDeadline, - MempoolPollingInterval: cfg.Bitcoind.TxPollingInterval, - PollingIntervalJitter: config.DefaultTxPollingJitter, - } - } - - bitcoindConn, err := chain.NewBitcoindConn(bitcoindCfg) - if err != nil { - return nil, err - } - - if err := bitcoindConn.Start(); err != nil { - return nil, fmt.Errorf("unable to connect to "+ - "bitcoind: %v", err) - } - - chainNotifier := bitcoindnotify.New( - bitcoindConn, params, hintCache, - hintCache, blockcache.NewBlockCache(cfg.Bitcoind.BlockCacheSize), - ) - - return &NodeBackend{ - ChainNotifier: chainNotifier, - }, nil - - case types.Btcd: - btcdUser := cfg.Btcd.RPCUser - btcdPass := cfg.Btcd.RPCPass - btcdHost := cfg.Btcd.RPCHost - - var certs []byte - if !cfg.Btcd.DisableTLS { - if cfg.Btcd.RawCert != "" { - decoded, err := hex.DecodeString(cfg.Btcd.RawCert) - if err != nil { - return nil, fmt.Errorf("error decoding btcd cert: %v", err) - } - certs = decoded + bitcoindCfg := &chain.BitcoindConfig{ + ChainParams: params, + Host: cfg.RPCHost, + User: cfg.RPCUser, + Pass: cfg.RPCPass, + Dialer: BuildDialer(cfg.RPCHost), + PrunedModeMaxPeers: cfg.PrunedNodeMaxPeers, + } - } else { - certificates, err := os.ReadFile(cfg.Btcd.RPCCert) - if err != nil { - return nil, fmt.Errorf("error reading btcd cert file: %v", err) - } - certs = certificates - } + if cfg.RPCPolling { + bitcoindCfg.PollingConfig = &chain.PollingConfig{ + BlockPollingInterval: cfg.BlockPollingInterval, + TxPollingInterval: cfg.TxPollingInterval, + TxPollingIntervalJitter: config.DefaultTxPollingJitter, } - - rpcConfig := &rpcclient.ConnConfig{ - Host: btcdHost, - Endpoint: "ws", - User: btcdUser, - Pass: btcdPass, - Certificates: certs, - DisableTLS: cfg.Btcd.DisableTLS, - DisableConnectOnNew: true, - DisableAutoReconnect: false, + } else { + bitcoindCfg.ZMQConfig = &chain.ZMQConfig{ + ZMQBlockHost: cfg.ZMQPubRawBlock, + ZMQTxHost: cfg.ZMQPubRawTx, + ZMQReadDeadline: cfg.ZMQReadDeadline, + MempoolPollingInterval: cfg.TxPollingInterval, + PollingIntervalJitter: config.DefaultTxPollingJitter, } + } - chainNotifier, err := btcdnotify.New( - rpcConfig, params, hintCache, - hintCache, blockcache.NewBlockCache(cfg.Btcd.BlockCacheSize), - ) + bitcoindConn, err := chain.NewBitcoindConn(bitcoindCfg) + if err != nil { + return nil, err + } - if err != nil { - return nil, err - } + if err := bitcoindConn.Start(); err != nil { + return nil, fmt.Errorf("unable to connect to "+ + "bitcoind: %v", err) + } - return &NodeBackend{ - ChainNotifier: chainNotifier, - }, nil + chainNotifier := bitcoindnotify.New( + bitcoindConn, params, hintCache, + hintCache, blockcache.NewBlockCache(cfg.BlockCacheSize), + ) - default: - return nil, fmt.Errorf("unknown node backend: %v", cfg.ActiveNodeBackend) - } + return &NodeBackend{ + ChainNotifier: chainNotifier, + }, nil } // NewNodeBackendWithParams creates a new NodeBackend by incorporating parameter retrieval and config conversion. -func NewNodeBackendWithParams(cfg config.BTCConfig, rawCert string) (*NodeBackend, error) { +func NewNodeBackendWithParams(cfg config.BTCConfig) (*NodeBackend, error) { btcParams, err := netparams.GetBTCParams(cfg.NetParams) if err != nil { return nil, fmt.Errorf("failed to get BTC net params: %w", err) } - btcNotifier, err := NewNodeBackend(CfgToBtcNodeBackendConfig(cfg, rawCert), btcParams, &EmptyHintCache{}) + btcNotifier, err := NewNodeBackend(ToBitcoindConfig(cfg), btcParams, &EmptyHintCache{}) if err != nil { return nil, fmt.Errorf("failed to initialize notifier: %w", err) } diff --git a/cmd/vigilante/cmd/btcstaking_tracker.go b/cmd/vigilante/cmd/btcstaking_tracker.go index d4794170..1874b753 100644 --- a/cmd/vigilante/cmd/btcstaking_tracker.go +++ b/cmd/vigilante/cmd/btcstaking_tracker.go @@ -68,7 +68,7 @@ func GetBTCStakingTracker() *cobra.Command { // create BTC notifier // TODO: is it possible to merge BTC client and BTC notifier? - btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC, "") + btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC) if err != nil { panic(err) } diff --git a/cmd/vigilante/cmd/monitor.go b/cmd/vigilante/cmd/monitor.go index 49259c50..fe2b7423 100644 --- a/cmd/vigilante/cmd/monitor.go +++ b/cmd/vigilante/cmd/monitor.go @@ -74,7 +74,7 @@ func GetMonitorCmd() *cobra.Command { monitorMetrics := metrics.NewMonitorMetrics() // create the chain notifier - btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC, "") + btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC) if err != nil { panic(err) } diff --git a/cmd/vigilante/cmd/reporter.go b/cmd/vigilante/cmd/reporter.go index 5c9a0b60..0d763d18 100644 --- a/cmd/vigilante/cmd/reporter.go +++ b/cmd/vigilante/cmd/reporter.go @@ -62,7 +62,7 @@ func GetReporterCmd() *cobra.Command { reporterMetrics := metrics.NewReporterMetrics() // create the chain notifier - btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC, "") + btcNotifier, err := btcclient.NewNodeBackendWithParams(cfg.BTC) if err != nil { panic(err) } diff --git a/config/bitcoin.go b/config/bitcoin.go index aa2b5e97..0b89ea07 100644 --- a/config/bitcoin.go +++ b/config/bitcoin.go @@ -3,8 +3,6 @@ package config import ( "errors" "fmt" - "os" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/babylonlabs-io/vigilante/types" @@ -12,27 +10,22 @@ import ( // BTCConfig defines configuration for the Bitcoin client type BTCConfig struct { - DisableClientTLS bool `mapstructure:"no-client-tls"` - CAFile string `mapstructure:"ca-file"` - Endpoint string `mapstructure:"endpoint"` - WalletEndpoint string `mapstructure:"wallet-endpoint"` - WalletPassword string `mapstructure:"wallet-password"` - WalletName string `mapstructure:"wallet-name"` - WalletCAFile string `mapstructure:"wallet-ca-file"` - WalletLockTime int64 `mapstructure:"wallet-lock-time"` // time duration in which the wallet remains unlocked, in seconds - TxFeeMin chainfee.SatPerKVByte `mapstructure:"tx-fee-min"` // minimum tx fee, sat/kvb - TxFeeMax chainfee.SatPerKVByte `mapstructure:"tx-fee-max"` // maximum tx fee, sat/kvb - DefaultFee chainfee.SatPerKVByte `mapstructure:"default-fee"` // default BTC tx fee in case estimation fails, sat/kvb - EstimateMode string `mapstructure:"estimate-mode"` // the BTC tx fee estimate mode, which is only used by bitcoind, must be either ECONOMICAL or CONSERVATIVE - TargetBlockNum int64 `mapstructure:"target-block-num"` // this implies how soon the tx is estimated to be included in a block, e.g., 1 means the tx is estimated to be included in the next block - NetParams string `mapstructure:"net-params"` - Username string `mapstructure:"username"` - Password string `mapstructure:"password"` - ReconnectAttempts int `mapstructure:"reconnect-attempts"` - BtcBackend types.SupportedBtcBackend `mapstructure:"btc-backend"` - ZmqSeqEndpoint string `mapstructure:"zmq-seq-endpoint"` - ZmqBlockEndpoint string `mapstructure:"zmq-block-endpoint"` - ZmqTxEndpoint string `mapstructure:"zmq-tx-endpoint"` + Endpoint string `mapstructure:"endpoint"` + WalletPassword string `mapstructure:"wallet-password"` + WalletName string `mapstructure:"wallet-name"` + WalletLockTime int64 `mapstructure:"wallet-lock-time"` // time duration in which the wallet remains unlocked, in seconds + TxFeeMin chainfee.SatPerKVByte `mapstructure:"tx-fee-min"` // minimum tx fee, sat/kvb + TxFeeMax chainfee.SatPerKVByte `mapstructure:"tx-fee-max"` // maximum tx fee, sat/kvb + DefaultFee chainfee.SatPerKVByte `mapstructure:"default-fee"` // default BTC tx fee in case estimation fails, sat/kvb + EstimateMode string `mapstructure:"estimate-mode"` // the BTC tx fee estimate mode, which is only used by bitcoind, must be either ECONOMICAL or CONSERVATIVE + TargetBlockNum int64 `mapstructure:"target-block-num"` // this implies how soon the tx is estimated to be included in a block, e.g., 1 means the tx is estimated to be included in the next block + NetParams string `mapstructure:"net-params"` + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` + ReconnectAttempts int `mapstructure:"reconnect-attempts"` + ZmqSeqEndpoint string `mapstructure:"zmq-seq-endpoint"` + ZmqBlockEndpoint string `mapstructure:"zmq-block-endpoint"` + ZmqTxEndpoint string `mapstructure:"zmq-tx-endpoint"` } func (cfg *BTCConfig) Validate() error { @@ -44,27 +37,21 @@ func (cfg *BTCConfig) Validate() error { return errors.New("invalid net params") } - if _, ok := types.GetValidBtcBackends()[cfg.BtcBackend]; !ok { - return errors.New("invalid btc backend") + // TODO: implement regex validation for zmq endpoint + if cfg.ZmqBlockEndpoint == "" { + return errors.New("zmq block endpoint cannot be empty") } - if cfg.BtcBackend == types.Bitcoind { - // TODO: implement regex validation for zmq endpoint - if cfg.ZmqBlockEndpoint == "" { - return errors.New("zmq block endpoint cannot be empty") - } - - if cfg.ZmqTxEndpoint == "" { - return errors.New("zmq tx endpoint cannot be empty") - } + if cfg.ZmqTxEndpoint == "" { + return errors.New("zmq tx endpoint cannot be empty") + } - if cfg.ZmqSeqEndpoint == "" { - return errors.New("zmq seq endpoint cannot be empty") - } + if cfg.ZmqSeqEndpoint == "" { + return errors.New("zmq seq endpoint cannot be empty") + } - if cfg.EstimateMode != "ECONOMICAL" && cfg.EstimateMode != "CONSERVATIVE" { - return errors.New("estimate-mode must be either ECONOMICAL or CONSERVATIVE when the backend is bitcoind") - } + if cfg.EstimateMode != "ECONOMICAL" && cfg.EstimateMode != "CONSERVATIVE" { + return errors.New("estimate-mode must be either ECONOMICAL or CONSERVATIVE when the backend is bitcoind") } if cfg.TargetBlockNum <= 0 { @@ -109,15 +96,10 @@ const ( func DefaultBTCConfig() BTCConfig { return BTCConfig{ - DisableClientTLS: false, - CAFile: defaultBtcCAFile, Endpoint: DefaultRpcBtcNodeHost, - WalletEndpoint: "localhost:18554", WalletPassword: "walletpass", WalletName: "default", - WalletCAFile: defaultBtcWalletCAFile, WalletLockTime: 10, - BtcBackend: types.Btcd, TxFeeMax: chainfee.SatPerKVByte(20 * 1000), // 20,000sat/kvb = 20sat/vbyte TxFeeMin: chainfee.SatPerKVByte(1 * 1000), // 1,000sat/kvb = 1sat/vbyte DefaultFee: chainfee.SatPerKVByte(1 * 1000), // 1,000sat/kvb = 1sat/vbyte @@ -132,35 +114,3 @@ func DefaultBTCConfig() BTCConfig { ZmqTxEndpoint: DefaultZmqTxEndpoint, } } - -func (cfg *BTCConfig) ReadCAFile() []byte { - if cfg.DisableClientTLS { - return nil - } - - // Read certificate file if TLS is not disabled. - certs, err := os.ReadFile(cfg.CAFile) - if err != nil { - // If there's an error reading the CA file, continue - // with nil certs and without the client connection. - return nil - } - - return certs -} - -func (cfg *BTCConfig) ReadWalletCAFile() []byte { - if cfg.DisableClientTLS { - // Chain server RPC TLS is disabled - return nil - } - - // Read certificate file if TLS is not disabled. - certs, err := os.ReadFile(cfg.WalletCAFile) - if err != nil { - // If there's an error reading the CA file, continue - // with nil certs and without the client connection. - return nil - } - return certs -} diff --git a/config/config.go b/config/config.go index 281c7976..f498f65a 100644 --- a/config/config.go +++ b/config/config.go @@ -18,12 +18,10 @@ const ( ) var ( - defaultBtcCAFile = filepath.Join(btcutil.AppDataDir("btcd", false), "rpc.cert") - defaultBtcWalletCAFile = filepath.Join(btcutil.AppDataDir("btcwallet", false), "rpc.cert") - defaultAppDataDir = btcutil.AppDataDir("babylon-vigilante", false) - defaultConfigFile = filepath.Join(defaultAppDataDir, defaultConfigFilename) - defaultRPCKeyFile = filepath.Join(defaultAppDataDir, "rpc.key") - defaultRPCCertFile = filepath.Join(defaultAppDataDir, "rpc.cert") + defaultAppDataDir = btcutil.AppDataDir("babylon-vigilante", false) + defaultConfigFile = filepath.Join(defaultAppDataDir, defaultConfigFilename) + defaultRPCKeyFile = filepath.Join(defaultAppDataDir, "rpc.key") + defaultRPCCertFile = filepath.Join(defaultAppDataDir, "rpc.cert") ) // Config defines the server's top level configuration diff --git a/e2etest/atomicslasher_e2e_test.go b/e2etest/atomicslasher_e2e_test.go index c9799026..ba56d6bd 100644 --- a/e2etest/atomicslasher_e2e_test.go +++ b/e2etest/atomicslasher_e2e_test.go @@ -36,10 +36,8 @@ func TestAtomicSlasher(t *testing.T) { emptyHintCache := btcclient.EmptyHintCache{} - // TODO: our config only support btcd wallet tls, not btcd directly - tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + btcclient.ToBitcoindConfig(tm.Config.BTC), &chaincfg.RegressionNetParams, &emptyHintCache, ) @@ -156,10 +154,8 @@ func TestAtomicSlasher_Unbonding(t *testing.T) { emptyHintCache := btcclient.EmptyHintCache{} - // TODO: our config only support btcd wallet tls, not btcd directly - tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + btcclient.ToBitcoindConfig(tm.Config.BTC), &chaincfg.RegressionNetParams, &emptyHintCache, ) diff --git a/e2etest/reporter_e2e_test.go b/e2etest/reporter_e2e_test.go index 9fec7d97..2d25fa35 100644 --- a/e2etest/reporter_e2e_test.go +++ b/e2etest/reporter_e2e_test.go @@ -65,7 +65,7 @@ func TestReporter_BoostrapUnderFrequentBTCHeaders(t *testing.T) { // create the chain notifier btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) require.NoError(t, err) - btcCfg := btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, "") + btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) require.NoError(t, err) @@ -128,7 +128,7 @@ func TestRelayHeadersAndHandleRollbacks(t *testing.T) { btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) require.NoError(t, err) - btcCfg := btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, "") + btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) require.NoError(t, err) @@ -179,7 +179,7 @@ func TestHandleReorgAfterRestart(t *testing.T) { btcParams, err := netparams.GetBTCParams(tm.Config.BTC.NetParams) require.NoError(t, err) - btcCfg := btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, "") + btcCfg := btcclient.ToBitcoindConfig(tm.Config.BTC) btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) require.NoError(t, err) diff --git a/e2etest/slasher_e2e_test.go b/e2etest/slasher_e2e_test.go index b9a94e3f..01a87b5c 100644 --- a/e2etest/slasher_e2e_test.go +++ b/e2etest/slasher_e2e_test.go @@ -28,10 +28,8 @@ func TestSlasher_GracefulShutdown(t *testing.T) { emptyHintCache := btcclient.EmptyHintCache{} - // TODO: our config only support btcd wallet tls, not btcd directly - tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + btcclient.ToBitcoindConfig(tm.Config.BTC), &chaincfg.RegressionNetParams, &emptyHintCache, ) @@ -80,10 +78,8 @@ func TestSlasher_Slasher(t *testing.T) { emptyHintCache := btcclient.EmptyHintCache{} - // TODO: our config only support btcd wallet tls, not btcd directly - tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + btcclient.ToBitcoindConfig(tm.Config.BTC), &chaincfg.RegressionNetParams, &emptyHintCache, ) @@ -150,10 +146,8 @@ func TestSlasher_SlashingUnbonding(t *testing.T) { emptyHintCache := btcclient.EmptyHintCache{} - // TODO: our config only support btcd wallet tls, not btcd directly - tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + btcclient.ToBitcoindConfig(tm.Config.BTC), &chaincfg.RegressionNetParams, &emptyHintCache, ) @@ -245,10 +239,9 @@ func TestSlasher_Bootstrapping(t *testing.T) { tm.VoteAndEquivocate(t, fpSK) emptyHintCache := btcclient.EmptyHintCache{} - // TODO: our config only support btcd wallet tls, not btcd directly - tm.Config.BTC.DisableClientTLS = false + backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + btcclient.ToBitcoindConfig(tm.Config.BTC), &chaincfg.RegressionNetParams, &emptyHintCache, ) diff --git a/e2etest/test_manager.go b/e2etest/test_manager.go index e7e2b003..d5b9936e 100644 --- a/e2etest/test_manager.go +++ b/e2etest/test_manager.go @@ -17,7 +17,6 @@ import ( btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" "github.com/babylonlabs-io/vigilante/btcclient" "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" @@ -39,16 +38,12 @@ var ( func defaultVigilanteConfig() *config.Config { defaultConfig := config.DefaultConfig() - // Config setting necessary to connect btcd daemon defaultConfig.BTC.NetParams = regtestParams.Name defaultConfig.BTC.Endpoint = "127.0.0.1:18443" // Config setting necessary to connect btcwallet daemon - defaultConfig.BTC.BtcBackend = types.Bitcoind - defaultConfig.BTC.WalletEndpoint = "127.0.0.1:18554" defaultConfig.BTC.WalletPassword = "pass" defaultConfig.BTC.Username = "user" defaultConfig.BTC.Password = "pass" - defaultConfig.BTC.DisableClientTLS = true defaultConfig.BTC.ZmqSeqEndpoint = config.DefaultZmqSeqEndpoint return defaultConfig diff --git a/e2etest/unbondingwatcher_e2e_test.go b/e2etest/unbondingwatcher_e2e_test.go index fc60c51f..d4ba0d98 100644 --- a/e2etest/unbondingwatcher_e2e_test.go +++ b/e2etest/unbondingwatcher_e2e_test.go @@ -30,10 +30,8 @@ func TestUnbondingWatcher(t *testing.T) { emptyHintCache := btcclient.EmptyHintCache{} - // TODO: our config only support btcd wallet tls, not btcd directly - tm.Config.BTC.DisableClientTLS = false backend, err := btcclient.NewNodeBackend( - btcclient.CfgToBtcNodeBackendConfig(tm.Config.BTC, ""), + btcclient.ToBitcoindConfig(tm.Config.BTC), &chaincfg.RegressionNetParams, &emptyHintCache, ) diff --git a/sample-vigilante.yml b/sample-vigilante.yml index 03d0b2da..076733e8 100644 --- a/sample-vigilante.yml +++ b/sample-vigilante.yml @@ -21,7 +21,6 @@ btc: username: rpcuser password: rpcpass reconnect-attempts: 3 - btc-backend: btcd # {btcd, bitcoind} zmq-endpoint: ~ # use tcp://127.0.0.1:29000 if btc-backend is bitcoind babylon: key: node0 diff --git a/submitter/relayer/estimator.go b/submitter/relayer/estimator.go index 33b2ec32..a9a395d4 100644 --- a/submitter/relayer/estimator.go +++ b/submitter/relayer/estimator.go @@ -7,59 +7,33 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/babylonlabs-io/vigilante/config" - "github.com/babylonlabs-io/vigilante/types" ) -// NewFeeEstimator creates a fee estimator based on the given backend -// currently, we only support bitcoind and btcd +// NewFeeEstimator creates a fee estimator for bitcoind func NewFeeEstimator(cfg *config.BTCConfig) (chainfee.Estimator, error) { + // TODO Currently we are not using Params field of rpcclient.ConnConfig due to bug in btcd + // when handling signet. + // todo(lazar955): check if we should start specifying this, considering we are no longer using btcd based on comment above ^^ + connCfg := &rpcclient.ConnConfig{ + // this will work with node loaded with multiple wallets + Host: cfg.Endpoint + "/wallet/" + cfg.WalletName, + HTTPPostMode: true, + User: cfg.Username, + Pass: cfg.Password, + DisableTLS: true, + } + + estimator, err := chainfee.NewBitcoindEstimator( + *connCfg, cfg.EstimateMode, cfg.DefaultFee.FeePerKWeight(), + ) - var connCfg *rpcclient.ConnConfig - var est chainfee.Estimator - switch cfg.BtcBackend { - case types.Bitcoind: - // TODO Currently we are not using Params field of rpcclient.ConnConfig due to bug in btcd - // when handling signet. - connCfg = &rpcclient.ConnConfig{ - // this will work with node loaded with multiple wallets - Host: cfg.Endpoint + "/wallet/" + cfg.WalletName, - HTTPPostMode: true, - User: cfg.Username, - Pass: cfg.Password, - DisableTLS: cfg.DisableClientTLS, - } - bitcoindEst, err := chainfee.NewBitcoindEstimator( - *connCfg, cfg.EstimateMode, cfg.DefaultFee.FeePerKWeight(), - ) - if err != nil { - return nil, fmt.Errorf("failed to create fee estimator for %s backend: %w", types.Bitcoind, err) - } - est = bitcoindEst - case types.Btcd: - // TODO Currently we are not using Params field of rpcclient.ConnConfig due to bug in btcd - // when handling signet. - connCfg = &rpcclient.ConnConfig{ - Host: cfg.WalletEndpoint, - Endpoint: "ws", // websocket - User: cfg.Username, - Pass: cfg.Password, - DisableTLS: cfg.DisableClientTLS, - Certificates: cfg.ReadWalletCAFile(), - } - btcdEst, err := chainfee.NewBtcdEstimator( - *connCfg, cfg.DefaultFee.FeePerKWeight(), - ) - if err != nil { - return nil, fmt.Errorf("failed to create fee estimator for %s backend: %w", types.Btcd, err) - } - est = btcdEst - default: - return nil, fmt.Errorf("unsupported backend for fee estimator") + if err != nil { + return nil, fmt.Errorf("failed to create fee estimator: %w", err) } - if err := est.Start(); err != nil { - return nil, fmt.Errorf("failed to initiate the fee estimator for %s backend: %w", cfg.BtcBackend, err) + if err := estimator.Start(); err != nil { + return nil, fmt.Errorf("failed to initiate the fee estimator: %w", err) } - return est, nil + return estimator, nil } diff --git a/submitter/submitter.go b/submitter/submitter.go index f689cb49..da1fd744 100644 --- a/submitter/submitter.go +++ b/submitter/submitter.go @@ -69,7 +69,7 @@ func New( if err != nil { return nil, fmt.Errorf("failed to create fee estimator: %w", err) } - logger.Sugar().Infof("Successfully started fee estimator for %s backend", btcCfg.BtcBackend) + logger.Sugar().Infof("Successfully started fee estimator for bitcoind") r := relayer.New( btcWallet, diff --git a/types/utils.go b/types/utils.go index 4529782e..bb39fd52 100644 --- a/types/utils.go +++ b/types/utils.go @@ -1,13 +1,12 @@ package types import ( - "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/wire" ) type ( SupportedBtcNetwork string - SupportedBtcBackend string ) const ( @@ -16,19 +15,12 @@ const ( BtcSimnet SupportedBtcNetwork = "simnet" BtcRegtest SupportedBtcNetwork = "regtest" BtcSignet SupportedBtcNetwork = "signet" - - Btcd SupportedBtcBackend = "btcd" - Bitcoind SupportedBtcBackend = "bitcoind" ) func (c SupportedBtcNetwork) String() string { return string(c) } -func (c SupportedBtcBackend) String() string { - return string(c) -} - func GetWrappedTxs(msg *wire.MsgBlock) []*btcutil.Tx { btcTxs := []*btcutil.Tx{} @@ -53,12 +45,3 @@ func GetValidNetParams() map[string]bool { return params } - -func GetValidBtcBackends() map[SupportedBtcBackend]bool { - validBtcBackends := map[SupportedBtcBackend]bool{ - Bitcoind: true, - Btcd: true, - } - - return validBtcBackends -} From 489293d7d0867da4456610232a23a98a8a693778 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:28:58 +0200 Subject: [PATCH 10/12] chore(relayer): prevent tx1 resubmission (#28) If tx2 fails and we are retrying, we shouldn't resend tx1 again. Referencing [issue](https://github.com/babylonchain/vigilante/issues/67) --- submitter/relayer/relayer.go | 157 +++++++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 42 deletions(-) diff --git a/submitter/relayer/relayer.go b/submitter/relayer/relayer.go index 1e23ebb0..4d167935 100644 --- a/submitter/relayer/relayer.go +++ b/submitter/relayer/relayer.go @@ -55,14 +55,15 @@ func New( ) *Relayer { metrics.ResendIntervalSecondsGauge.Set(float64(config.ResendIntervalSeconds)) return &Relayer{ - Estimator: est, - BTCWallet: wallet, - tag: tag, - version: version, - submitterAddress: submitterAddress, - metrics: metrics, - config: config, - logger: parentLogger.With(zap.String("module", "relayer")).Sugar(), + Estimator: est, + BTCWallet: wallet, + tag: tag, + version: version, + submitterAddress: submitterAddress, + metrics: metrics, + config: config, + lastSubmittedCheckpoint: &types.CheckpointInfo{}, + logger: parentLogger.With(zap.String("module", "relayer")).Sugar(), } } @@ -80,8 +81,8 @@ func (rl *Relayer) SendCheckpointToBTC(ckpt *ckpttypes.RawCheckpointWithMetaResp return nil } - if rl.lastSubmittedCheckpoint == nil || rl.lastSubmittedCheckpoint.Epoch < ckptEpoch { - rl.logger.Infof("Submitting a raw checkpoint for epoch %v for the first time", ckptEpoch) + if rl.shouldSendCompleteCkpt(ckptEpoch) { + rl.logger.Infof("Submitting a raw checkpoint for epoch %v", ckptEpoch) submittedCheckpoint, err := rl.convertCkptToTwoTxAndSubmit(ckpt.Ckpt) if err != nil { @@ -90,6 +91,16 @@ func (rl *Relayer) SendCheckpointToBTC(ckpt *ckpttypes.RawCheckpointWithMetaResp rl.lastSubmittedCheckpoint = submittedCheckpoint + return nil + } else if rl.shouldSendTx2(ckptEpoch) { + rl.logger.Infof("Retrying to send tx2 for epoch %v, tx1 %s", ckptEpoch, rl.lastSubmittedCheckpoint.Tx1.TxId) + submittedCheckpoint, err := rl.retrySendTx2(ckpt.Ckpt) + if err != nil { + return err + } + + rl.lastSubmittedCheckpoint = submittedCheckpoint + return nil } @@ -144,6 +155,16 @@ func (rl *Relayer) SendCheckpointToBTC(ckpt *ckpttypes.RawCheckpointWithMetaResp return nil } +func (rl *Relayer) shouldSendCompleteCkpt(ckptEpoch uint64) bool { + return rl.lastSubmittedCheckpoint.Tx1 == nil || rl.lastSubmittedCheckpoint.Epoch < ckptEpoch +} + +// shouldSendTx2 - we want to avoid resending tx1 if only tx2 submission has failed +func (rl *Relayer) shouldSendTx2(ckptEpoch uint64) bool { + return (rl.lastSubmittedCheckpoint.Tx1 != nil || rl.lastSubmittedCheckpoint.Epoch < ckptEpoch) && + rl.lastSubmittedCheckpoint.Tx2 == nil +} + // shouldResendCheckpoint checks whether the bumpedFee is effective for replacement func (rl *Relayer) shouldResendCheckpoint(ckptInfo *types.CheckpointInfo, bumpedFee btcutil.Amount) bool { // if the bumped fee is less than the fee of the previous second tx plus the minimum required bumping fee @@ -238,49 +259,67 @@ func (rl *Relayer) signTx(tx *wire.MsgTx) (*wire.MsgTx, error) { return signedTx, nil } -func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResponse) (*types.CheckpointInfo, error) { +func (rl *Relayer) encodeCheckpointData(ckpt *ckpttypes.RawCheckpointResponse) ([]byte, []byte, error) { + // Convert to raw checkpoint rawCkpt, err := ckpt.ToRawCheckpoint() if err != nil { - return nil, err + return nil, nil, err } + + // Convert raw checkpoint to BTC checkpoint btcCkpt, err := ckpttypes.FromRawCkptToBTCCkpt(rawCkpt, rl.submitterAddress) if err != nil { - return nil, err + return nil, nil, err } + + // Encode checkpoint data data1, data2, err := btctxformatter.EncodeCheckpointData( rl.tag, rl.version, btcCkpt, ) if err != nil { - return nil, err - } - - tx1, tx2, err := rl.ChainTwoTxAndSend(data1, data2) - if err != nil { - return nil, err + return nil, nil, err } - // this is to wait for btcwallet to update utxo database so that - // the tx that tx1 consumes will not appear in the next unspent txs lit - time.Sleep(1 * time.Second) + // Return the encoded data + return data1, data2, nil +} +func (rl *Relayer) logAndRecordCheckpointMetrics(tx1, tx2 *types.BtcTxInfo, epochNum uint64) { + // Log the transactions sent for checkpointing rl.logger.Infof("Sent two txs to BTC for checkpointing epoch %v, first txid: %s, second txid: %s", - ckpt.EpochNum, tx1.Tx.TxHash().String(), tx2.Tx.TxHash().String()) + epochNum, tx1.Tx.TxHash().String(), tx2.Tx.TxHash().String()) - // record metrics of the two transactions + // Record metrics for the first transaction rl.metrics.NewSubmittedCheckpointSegmentGaugeVec.WithLabelValues( - strconv.Itoa(int(ckpt.EpochNum)), + strconv.Itoa(int(epochNum)), "0", tx1.Tx.TxHash().String(), strconv.Itoa(int(tx1.Fee)), ).SetToCurrentTime() + + // Record metrics for the second transaction rl.metrics.NewSubmittedCheckpointSegmentGaugeVec.WithLabelValues( - strconv.Itoa(int(ckpt.EpochNum)), + strconv.Itoa(int(epochNum)), "1", tx2.Tx.TxHash().String(), strconv.Itoa(int(tx2.Fee)), ).SetToCurrentTime() +} + +func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResponse) (*types.CheckpointInfo, error) { + data1, data2, err := rl.encodeCheckpointData(ckpt) + if err != nil { + return nil, err + } + + tx1, tx2, err := rl.ChainTwoTxAndSend(data1, data2) + if err != nil { + return nil, err + } + + rl.logAndRecordCheckpointMetrics(tx1, tx2, ckpt.EpochNum) return &types.CheckpointInfo{ Epoch: ckpt.EpochNum, @@ -290,34 +329,68 @@ func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResp }, nil } -// ChainTwoTxAndSend builds two chaining txs with the given data: -// the second tx consumes the output of the first tx -func (rl *Relayer) ChainTwoTxAndSend(data1 []byte, data2 []byte) (*types.BtcTxInfo, *types.BtcTxInfo, error) { - // recipient is a change address that all the - // remaining balance of the utxo is sent to - tx1, err := rl.buildTxWithData(data1, nil) +// retrySendTx2 - rebuilds the tx2 and sends it, expects that tx1 has been sent and +// lastSubmittedCheckpoint.Tx1 is not nil +func (rl *Relayer) retrySendTx2(ckpt *ckpttypes.RawCheckpointResponse) (*types.CheckpointInfo, error) { + _, data2, err := rl.encodeCheckpointData(ckpt) if err != nil { - return nil, nil, fmt.Errorf("failed to add data to tx1: %w", err) + return nil, err } - tx1.TxId, err = rl.sendTxToBTC(tx1.Tx) + tx1 := rl.lastSubmittedCheckpoint.Tx1 + if tx1 == nil { + return nil, fmt.Errorf("tx1 is nil") // shouldn't happen, sanity check + } + + tx2, err := rl.buildAndSendTx(data2, tx1.Tx) + if err != nil { + return nil, err + } + + rl.logAndRecordCheckpointMetrics(tx1, tx2, ckpt.EpochNum) + + return &types.CheckpointInfo{ + Epoch: ckpt.EpochNum, + Ts: time.Now(), + Tx1: tx1, + Tx2: tx2, + }, nil +} + +// buildAndSendTx helper function to build and send a transaction +func (rl *Relayer) buildAndSendTx(data []byte, parentTx *wire.MsgTx) (*types.BtcTxInfo, error) { + tx, err := rl.buildTxWithData(data, parentTx) if err != nil { - return nil, nil, fmt.Errorf("failed to send tx1 to BTC: %w", err) + return nil, fmt.Errorf("failed to add data to tx: %w", err) } - // the second tx consumes the second output (index 1) - // of the first tx, as the output at index 0 is OP_RETURN - tx2, err := rl.buildTxWithData(data2, tx1.Tx) + tx.TxId, err = rl.sendTxToBTC(tx.Tx) if err != nil { - return nil, nil, fmt.Errorf("failed to add data to tx2: %w", err) + return nil, fmt.Errorf("failed to send tx to BTC: %w", err) } - tx2.TxId, err = rl.sendTxToBTC(tx2.Tx) + return tx, nil +} + +// ChainTwoTxAndSend builds two chaining txs with the given data: +// the second tx consumes the output of the first tx +func (rl *Relayer) ChainTwoTxAndSend(data1 []byte, data2 []byte) (*types.BtcTxInfo, *types.BtcTxInfo, error) { + // recipient is a change address that all the + // remaining balance of the utxo is sent to + + tx1, err := rl.buildAndSendTx(data1, nil) if err != nil { - return nil, nil, fmt.Errorf("failed to send tx2 to BTC: %w", err) + return nil, nil, err } - // TODO: if tx1 succeeds but tx2 fails, we should not resent tx1 + // cache the success of tx1, we need it if we fail with tx2 send + rl.lastSubmittedCheckpoint.Tx1 = tx1 + + // Build and send tx2, using tx1 as the parent + tx2, err := rl.buildAndSendTx(data2, tx1.Tx) + if err != nil { + return nil, nil, err + } return tx1, tx2, nil } From 263591a98885d969d7b50e312f1b1ef6c88c045c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:02:23 +0200 Subject: [PATCH 11/12] chore(deps): bump github.com/ethereum/go-ethereum from 1.13.14 to 1.13.15 (#17) Bumps [github.com/ethereum/go-ethereum](https://github.com/ethereum/go-ethereum) from 1.13.14 to 1.13.15.
Release notes

Sourced from github.com/ethereum/go-ethereum's releases.

Ontamalca (v1.13.15)

Geth v1.13.15 is a maintenance-release that contains some fixes mainly to avoid snapsync-related data-corruption.

We recommend all users to upgrade to v1.13.15 as soon as possible.


As with all our previous releases, you can find the:

Commits
  • c5ba367 params: release Geth v 1.13.15
  • 35e0525 core, eth/protocols/snap, trie: fix cause for snap-sync corruption, implement...
  • 7bcb553 eth/filters: enforce topic-limit early on filter criterias (#29535)
  • e343ddf core/rawdb: add sanity-limit to header accessor (#29534)
  • 5dcf503 eth/protocols/snap: skip retrieval for completed storages (#29378)
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/ethereum/go-ethereum&package-manager=go_modules&previous-version=1.13.14&new-version=1.13.15)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/babylonlabs-io/vigilante/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fb2d502d..71a53725 100644 --- a/go.mod +++ b/go.mod @@ -135,7 +135,7 @@ require ( github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/emicklei/dot v1.6.1 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect - github.com/ethereum/go-ethereum v1.13.14 // indirect + github.com/ethereum/go-ethereum v1.13.15 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fergusstrange/embedded-postgres v1.10.0 // indirect diff --git a/go.sum b/go.sum index 8ff2ee2b..aa5d1149 100644 --- a/go.sum +++ b/go.sum @@ -550,8 +550,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU 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/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= +github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= From 6448d1f6ac51c5419134acb96784a52468758ec4 Mon Sep 17 00:00:00 2001 From: Lazar <12626340+Lazar955@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:57:30 +0200 Subject: [PATCH 12/12] chore(*): switch to trunk based dev (#30) Aligning vigilante repo with babylon dev process --- .github/workflows/publish.yml | 1 - CONTRIBUTING.md | 5 +++++ RELEASE_PROCESS.md | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md create mode 100644 RELEASE_PROCESS.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e96ee865..c7e1dce4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,7 +4,6 @@ on: push: branches: - 'main' - - 'dev' tags: - '*' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..1e327011 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing + +Vigilante repository follows the same contributing rules as +[Babylon node](https://github.com/babylonlabs-io/babylon/blob/main/CONTRIBUTING.md) +repository. \ No newline at end of file diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md new file mode 100644 index 00000000..61014695 --- /dev/null +++ b/RELEASE_PROCESS.md @@ -0,0 +1,5 @@ +# Release Process + +Vigilate repository follows the same release process rules as +[Babylon node](https://github.com/babylonlabs-io/babylon/blob/main/RELEASE_PROCESS.md) +repository. \ No newline at end of file