Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(e2e): removing btcd #9

Merged
merged 36 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5b41398
adds bitcoind node setup
Lazar955 Aug 1, 2024
b57490e
wip removing btcd
Lazar955 Aug 2, 2024
f476f25
test manager changes
Lazar955 Aug 2, 2024
45cef93
wip atomic slasher test passes
Lazar955 Aug 5, 2024
015c113
makes TestAtomicSlasher work
Lazar955 Aug 6, 2024
1797ff8
fixes TestAtomicSlasher_Unbonding
Lazar955 Aug 6, 2024
bb9fce1
fixes TestSlasher_GracefulShutdown
Lazar955 Aug 6, 2024
272b0b1
fixes TestSlasher_Slasher
Lazar955 Aug 6, 2024
a6757e3
fixes TestSlasher_SlashingUnbonding
Lazar955 Aug 6, 2024
963fb2e
makes TestSlasher_Bootstrapping work
Lazar955 Aug 6, 2024
a01b899
fixes TestUnbondingWatcher
Lazar955 Aug 7, 2024
80d0c65
fixes submitter tests
Lazar955 Aug 7, 2024
e31b37f
wip reporter e2e
Lazar955 Aug 7, 2024
9080db3
adds lock for block generation
Lazar955 Aug 7, 2024
08499c7
clean up
Lazar955 Aug 8, 2024
9d99a68
zmq works, fixes TestReporter_BoostrapUnderFrequentBTCHeaders
Lazar955 Aug 9, 2024
6ec7ca7
fixes all reporter tests
Lazar955 Aug 9, 2024
25399ad
wip repotrer e2e
Lazar955 Aug 9, 2024
a7c9b2f
remove backend check
Lazar955 Aug 9, 2024
bc181f2
cleanup slasher
Lazar955 Aug 9, 2024
a982c73
cleanup manager
Lazar955 Aug 12, 2024
74a2aeb
remove btc wallet client
Lazar955 Aug 12, 2024
c64c258
cleanup manager
Lazar955 Aug 12, 2024
b385d6c
linting
Lazar955 Aug 12, 2024
9dd8221
minor: fix Makefile (#3)
SebastianElvis Aug 7, 2024
bf4c5f2
Bump Babylon to version with hot fix for retries
KonradStaniec Aug 7, 2024
94b58b6
switch to gh actions (#5)
KonradStaniec Aug 7, 2024
b18efcc
try fix ci
Lazar955 Aug 12, 2024
0dcc8b6
Merge branch 'dev' into lazar/e2e-bitcoind
Lazar955 Aug 12, 2024
87145bd
ensure go routine completion in test
Lazar955 Aug 12, 2024
f19dd2e
be more graceful shutting down
Lazar955 Aug 12, 2024
a6110c0
remove unused code
Lazar955 Aug 12, 2024
0b0535c
updates go mod
Lazar955 Aug 12, 2024
ca35e3d
uses regtest everywhere
Lazar955 Aug 12, 2024
d06cfe8
log cleanup
Lazar955 Aug 13, 2024
82007e0
cleanup
Lazar955 Aug 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions btcclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
9 changes: 0 additions & 9 deletions btcclient/client_block_subscriber.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

RafilxTenfen marked this conversation as resolved.
Show resolved Hide resolved
zmqClient, err := zmq.New(logger, cfg.ZmqSeqEndpoint, client.blockEventChan, rpcClient)
if err != nil {
return nil, err
Expand Down
3 changes: 1 addition & 2 deletions btcclient/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion config/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down
136 changes: 48 additions & 88 deletions e2etest/atomicslasher_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
package e2etest

import (
"encoding/hex"
"go.uber.org/zap"
"testing"
"time"

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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(
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -213,26 +171,21 @@ 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,
backend,
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
Expand All @@ -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
Expand All @@ -262,6 +216,8 @@ func TestAtomicSlasher_Unbonding(t *testing.T) {
}
}

require.NotNil(t, btcDel2, "err second delegation not found")

/*
the victim BTC delegation unbonds
*/
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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))
}
Loading
Loading