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

andomnes commit replay test #344

Merged
merged 4 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
55 changes: 55 additions & 0 deletions test/replay/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
btckckpttypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types"
ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types"
et "github.com/babylonlabs-io/babylon/x/epoching/types"
ftypes "github.com/babylonlabs-io/babylon/x/finality/types"

"cosmossdk.io/log"
"cosmossdk.io/math"
Expand Down Expand Up @@ -680,6 +681,42 @@ func (d *BabylonAppDriver) GetBTCCkptParams(t *testing.T) btckckpttypes.Params {
return d.App.BtcCheckpointKeeper.GetParams(d.GetContextForLastFinalizedBlock())
}

func (d *BabylonAppDriver) ProgressTillFirstBlockTheNextEpoch(t *testing.T) {
currnetEpochNunber := d.GetEpoch().EpochNumber
nextEpochNumber := currnetEpochNunber + 1

for currnetEpochNunber < nextEpochNumber {
d.GenerateNewBlock(t)
currnetEpochNunber = d.GetEpoch().EpochNumber
}
}

func (d *BabylonAppDriver) GetActiveFpsAtHeight(t *testing.T, height uint64) []*ftypes.ActiveFinalityProvidersAtHeightResponse {
res, err := d.App.FinalityKeeper.ActiveFinalityProvidersAtHeight(
d.GetContextForLastFinalizedBlock(),
&ftypes.QueryActiveFinalityProvidersAtHeightRequest{
Height: height,
Pagination: &query.PageRequest{},
},
)
require.NoError(t, err)
return res.FinalityProviders
}

func (d *BabylonAppDriver) GetActiveFpsAtCurrentHeight(t *testing.T) []*ftypes.ActiveFinalityProvidersAtHeightResponse {
return d.GetActiveFpsAtHeight(t, d.GetLastFinalizedBlock().Height)
}

func (d *BabylonAppDriver) WaitTillAllFpsJailed(t *testing.T) {
for {
activeFps := d.GetActiveFpsAtCurrentHeight(t)
if len(activeFps) == 0 {
break
}
d.GenerateNewBlock(t)
}
}

// SendTxWithMsgsFromDriverAccount sends tx with msgs from driver account and asserts that
// SendTxWithMsgsFromDriverAccount sends tx with msgs from driver account and asserts that
// execution was successful. It assumes that there will only be one tx in the block.
Expand Down Expand Up @@ -838,6 +875,14 @@ func GenerateNFinalityProviders(
return infos
}

func FpInfosToMsgs(fpInfos []*FinalityProviderInfo) []sdk.Msg {
msgs := []sdk.Msg{}
for _, fpInfo := range fpInfos {
msgs = append(msgs, fpInfo.MsgCreateFinalityProvider)
}
return msgs
}

func GenerateNBTCDelegationsForFinalityProvider(
r *rand.Rand,
t *testing.T,
Expand Down Expand Up @@ -878,6 +923,16 @@ func DelegationInfosToCreateBTCDelegationMsgs(
return msgs
}

func DelegationInfosToCovenantSignaturesMsgs(
delInfos []*datagen.CreateDelegationInfo,
) []sdk.Msg {
msgs := []sdk.Msg{}
for _, delInfo := range delInfos {
msgs = append(msgs, MsgsToSdkMsg(delInfo.MsgAddCovenantSigs)...)
}
return msgs
}

func DelegationInfosToBTCTx(
delInfos []*datagen.CreateDelegationInfo,
) []*wire.MsgTx {
Expand Down
215 changes: 215 additions & 0 deletions test/replay/finality_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package replay

import (
"math/rand"
"testing"
"time"

"github.com/babylonlabs-io/babylon/testutil/datagen"
bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types"
ftypes "github.com/babylonlabs-io/babylon/x/finality/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)

func TestVoting(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
numFinalityProviders := uint32(datagen.RandomInRange(r, 3, 4))
numDelPerFp := uint32(2)
driverTempDir := t.TempDir()
replayerTempDir := t.TempDir()
driver := NewBabylonAppDriver(t, driverTempDir, replayerTempDir)

driver.GenerateNewBlock(t)

stakingParams := driver.GetBTCStakingParams(t)

fpInfos := GenerateNFinalityProviders(r, t, numFinalityProviders, driver.GetDriverAccountAddress())
registerMsgs := FpInfosToMsgs(fpInfos)
driver.SendTxWithMsgsFromDriverAccount(t, registerMsgs...)

var msgList []*ftypes.MsgCommitPubRandList
for _, fpInfo := range fpInfos {
_, msg, err := datagen.GenRandomMsgCommitPubRandList(r, fpInfo.BTCPrivateKey, 1, 1000)
require.NoError(t, err)
msg.Signer = driver.GetDriverAccountAddress().String()
msgList = append(msgList, msg)
}
// send all commit randomness messages in one block
driver.SendTxWithMsgsFromDriverAccount(t, MsgsToSdkMsg(msgList)...)
currnetEpochNunber := driver.GetEpoch().EpochNumber
driver.ProgressTillFirstBlockTheNextEpoch(t)

driver.FinializeCkptForEpoch(r, t, currnetEpochNunber)

// at this point randomness is finalized, after sending delegation, finality
// providers will have delegations
var allDelegationInfos []*datagen.CreateDelegationInfo

for _, fpInfo := range fpInfos {
delInfos := GenerateNBTCDelegationsForFinalityProvider(
r,
t,
numDelPerFp,
driver.GetDriverAccountAddress(),
fpInfo,
stakingParams,
)
allDelegationInfos = append(allDelegationInfos, delInfos...)
}

driver.SendTxWithMsgsFromDriverAccount(t, MsgsToSdkMsg(DelegationInfosToCreateBTCDelegationMsgs(allDelegationInfos))...)
driver.SendTxWithMsgsFromDriverAccount(t, MsgsToSdkMsg(DelegationInfosToCovenantSignaturesMsgs(allDelegationInfos))...)

// all delegations are verified after activation finality provider should
// have voting power
stakingTransactions := DelegationInfosToBTCTx(allDelegationInfos)
blockWithProofs := driver.GenBlockWithTransactions(
r,
t,
stakingTransactions,
)
// make staking txs k-deep
driver.ExtendBTCLcWithNEmptyBlocks(r, t, 10)

activationMsgs := BlockWithProofsToActivationMessages(blockWithProofs, driver.GetDriverAccountAddress())

activeFps := driver.GetActiveFpsAtCurrentHeight(t)
require.Equal(t, 0, len(activeFps))

driver.SendTxWithMsgsFromDriverAccount(t, activationMsgs...)

// on the last block all power events were queued for execution
// after this block execution they should be processed and our fps should
// have voting power
driver.GenerateNewBlock(t)

activeFps = driver.GetActiveFpsAtCurrentHeight(t)
require.Equal(t, numFinalityProviders, uint32(len(activeFps)))

driver.WaitTillAllFpsJailed(t)
driver.GenerateNewBlock(t)
activeFps = driver.GetActiveFpsAtCurrentHeight(t)
require.Equal(t, 0, len(activeFps))

// Replay all the blocks from driver and check appHash
replayer := NewBlockReplayer(t, replayerTempDir)
replayer.ReplayBlocks(t, driver.FinalizedBlocks)
// after replay we should have the same apphash
require.Equal(t, driver.LastState.LastBlockHeight, replayer.LastState.LastBlockHeight)
require.Equal(t, driver.LastState.AppHash, replayer.LastState.AppHash)
}

func FuzzJailing(f *testing.F) {
datagen.AddRandomSeedsToFuzzer(f, 5)

f.Fuzz(func(t *testing.T, seed int64) {
t.Parallel()
r := rand.New(rand.NewSource(seed))
numFinalityProviders := uint32(datagen.RandomInRange(r, 3, 4))
numDelPerFp := uint32(2)
driverTempDir := t.TempDir()
replayerTempDir := t.TempDir()
driver := NewBabylonAppDriver(t, driverTempDir, replayerTempDir)

driver.GenerateNewBlock(t)

stakingParams := driver.GetBTCStakingParams(t)

fpInfos := GenerateNFinalityProviders(r, t, numFinalityProviders, driver.GetDriverAccountAddress())
registerMsgs := FpInfosToMsgs(fpInfos)
driver.SendTxWithMsgsFromDriverAccount(t, registerMsgs...)

var msgList []*ftypes.MsgCommitPubRandList
for _, fpInfo := range fpInfos {
_, msg, err := datagen.GenRandomMsgCommitPubRandList(r, fpInfo.BTCPrivateKey, 1, 1000)
require.NoError(t, err)
msg.Signer = driver.GetDriverAccountAddress().String()
msgList = append(msgList, msg)
}
// send all commit randomness messages in one block
driver.SendTxWithMsgsFromDriverAccount(t, MsgsToSdkMsg(msgList)...)
currnetEpochNunber := driver.GetEpoch().EpochNumber
driver.ProgressTillFirstBlockTheNextEpoch(t)

driver.FinializeCkptForEpoch(r, t, currnetEpochNunber)

// at this point randomness is finalized, after sending delegation, finality
// providers will have delegations
var allDelegationInfos []*datagen.CreateDelegationInfo

for _, fpInfo := range fpInfos {
delInfos := GenerateNBTCDelegationsForFinalityProvider(
r,
t,
numDelPerFp,
driver.GetDriverAccountAddress(),
fpInfo,
stakingParams,
)
allDelegationInfos = append(allDelegationInfos, delInfos...)
}

driver.SendTxWithMsgsFromDriverAccount(t, MsgsToSdkMsg(DelegationInfosToCreateBTCDelegationMsgs(allDelegationInfos))...)
driver.SendTxWithMsgsFromDriverAccount(t, MsgsToSdkMsg(DelegationInfosToCovenantSignaturesMsgs(allDelegationInfos))...)

// all delegations are verified after activation finality provider should
// have voting power
stakingTransactions := DelegationInfosToBTCTx(allDelegationInfos)
blockWithProofs := driver.GenBlockWithTransactions(
r,
t,
stakingTransactions,
)
// make staking txs k-deep
driver.ExtendBTCLcWithNEmptyBlocks(r, t, 10)

activationMsgs := BlockWithProofsToActivationMessages(blockWithProofs, driver.GetDriverAccountAddress())

activeFps := driver.GetActiveFpsAtCurrentHeight(t)
require.Equal(t, 0, len(activeFps))

driver.SendTxWithMsgsFromDriverAccount(t, activationMsgs...)

// on the last block all power events were queued for execution
// after this block execution they should be processed and our fps should
// have voting power
driver.GenerateNewBlock(t)

activeFps = driver.GetActiveFpsAtCurrentHeight(t)
require.Equal(t, numFinalityProviders, uint32(len(activeFps)))

driver.WaitTillAllFpsJailed(t)
driver.GenerateNewBlock(t)
activeFps = driver.GetActiveFpsAtCurrentHeight(t)
require.Equal(t, 0, len(activeFps))

// Replay all the blocks from driver and check appHash
replayer := NewBlockReplayer(t, replayerTempDir)
replayer.ReplayBlocks(t, driver.FinalizedBlocks)
// after replay we should have the same apphash
require.Equal(t, driver.LastState.LastBlockHeight, replayer.LastState.LastBlockHeight)
require.Equal(t, driver.LastState.AppHash, replayer.LastState.AppHash)
})
}

func BlockWithProofsToActivationMessages(
blockWithProofs *datagen.BlockWithProofs,
senderAddr sdk.AccAddress,
) []sdk.Msg {
msgs := []sdk.Msg{}

for i, tx := range blockWithProofs.Transactions {
// no coinbase tx
if i == 0 {
continue
}

msgs = append(msgs, &bstypes.MsgAddBTCDelegationInclusionProof{
Signer: senderAddr.String(),
StakingTxHash: tx.TxHash().String(),
StakingTxInclusionProof: bstypes.NewInclusionProofFromSpvProof(blockWithProofs.Proofs[i]),
})
}
return msgs
}
10 changes: 6 additions & 4 deletions testutil/datagen/btc_blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ func GenRandomBtcdBlock(r *rand.Rand, numBabylonTxs int, prevHash *chainhash.Has

// BTC block with proofs of each tx. Index of txs in the block is the same as the index of proofs.
type BlockWithProofs struct {
Block *wire.MsgBlock
Proofs []*btcctypes.BTCSpvProof
Block *wire.MsgBlock
Proofs []*btcctypes.BTCSpvProof
Transactions []*wire.MsgTx
}

func GenRandomBtcdBlockWithTransactions(
Expand Down Expand Up @@ -118,8 +119,9 @@ func GenRandomBtcdBlockWithTransactions(
Transactions: msgTxs,
}
return &BlockWithProofs{
Block: block,
Proofs: proofs,
Block: block,
Proofs: proofs,
Transactions: msgTxs,
}
}

Expand Down
Loading