Skip to content

Commit

Permalink
chore: Make injected checkpoint a standard tx (#224)
Browse files Browse the repository at this point in the history
Closes #129 by introducing the encoder and decoder into the proposal
handler
  • Loading branch information
gitferry authored Oct 23, 2024
1 parent 9efe79b commit 0831dc6
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 101 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

### State Machine Breaking

* [#224](https://github.com/babylonlabs-io/babylon/pull/224) Make injected checkpoint a standard tx
* [#207](https://github.com/babylonlabs-io/babylon/pull/207) Rename total voting power
to total bonded sat
* [#204](https://github.com/babylonlabs-io/babylon/pull/204) Add babylon finality
Expand Down
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ func NewBabylonApp(

// set proposal extension
proposalHandler := checkpointing.NewProposalHandler(
logger, &app.CheckpointingKeeper, bApp.Mempool(), bApp)
logger, &app.CheckpointingKeeper, bApp.Mempool(), bApp, app.EncCfg)
proposalHandler.SetHandlers(bApp)

// set vote extension
Expand Down
6 changes: 4 additions & 2 deletions proto/babylon/checkpointing/v1/checkpoint.proto
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ message RawCheckpointWithMeta {
repeated CheckpointStateUpdate lifecycle = 5;
}

// InjectedCheckpoint wraps the checkpoint and the extended votes
message InjectedCheckpoint {
// MsgInjectedCheckpoint wraps the checkpoint and the extended votes
// Note: this is a special message type that is only for internal ABCI++ usage
// for inserting checkpoint into the block
message MsgInjectedCheckpoint {
RawCheckpointWithMeta ckpt = 1;
// extended_commit_info is the commit info including the vote extensions
// from the previous proposal
Expand Down
64 changes: 45 additions & 19 deletions x/checkpointing/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@ import (
"slices"

"cosmossdk.io/log"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/mempool"

abci "github.com/cometbft/cometbft/abci/types"

appparams "github.com/babylonlabs-io/babylon/app/params"
ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types"
)

const defaultInjectedTxIndex = 0

type ProposalHandler struct {
logger log.Logger
ckptKeeper CheckpointingKeeper
txVerifier baseapp.ProposalTxVerifier
logger log.Logger
ckptKeeper CheckpointingKeeper
bApp *baseapp.BaseApp

// used for building and parsing the injected tx
txEncoder sdk.TxEncoder
txDecoder sdk.TxDecoder
txBuilder client.TxBuilder

defaultPrepareProposalHandler sdk.PrepareProposalHandler
defaultProcessProposalHandler sdk.ProcessProposalHandler
}
Expand All @@ -30,13 +37,19 @@ func NewProposalHandler(
logger log.Logger,
ckptKeeper CheckpointingKeeper,
mp mempool.Mempool,
txVerifier baseapp.ProposalTxVerifier,
bApp *baseapp.BaseApp,
encCfg *appparams.EncodingConfig,
) *ProposalHandler {
defaultHandler := baseapp.NewDefaultProposalHandler(mp, txVerifier)
defaultHandler := baseapp.NewDefaultProposalHandler(mp, bApp)
ckpttypes.RegisterInterfaces(encCfg.InterfaceRegistry)

return &ProposalHandler{
logger: logger,
ckptKeeper: ckptKeeper,
txVerifier: txVerifier,
bApp: bApp,
txEncoder: encCfg.TxConfig.TxEncoder(),
txDecoder: encCfg.TxConfig.TxDecoder(),
txBuilder: encCfg.TxConfig.NewTxBuilder(),
defaultPrepareProposalHandler: defaultHandler.PrepareProposalHandler(),
defaultProcessProposalHandler: defaultHandler.ProcessProposalHandler(),
}
Expand Down Expand Up @@ -91,11 +104,11 @@ func (h *ProposalHandler) PrepareProposal() sdk.PrepareProposalHandler {
}

// 3. inject a "fake" tx into the proposal s.t. validators can decode, verify the checkpoint
injectedCkpt := &ckpttypes.InjectedCheckpoint{
injectedCkpt := &ckpttypes.MsgInjectedCheckpoint{
Ckpt: ckpt,
ExtendedCommitInfo: &req.LocalLastCommit,
}
injectedVoteExtTx, err := injectedCkpt.Marshal()
injectedVoteExtTx, err := h.buildInjectedTxBytes(injectedCkpt)
if err != nil {
return nil, fmt.Errorf("failed to encode vote extensions into a special tx: %w", err)
}
Expand Down Expand Up @@ -268,7 +281,7 @@ func (h *ProposalHandler) ProcessProposal() sdk.ProcessProposalHandler {
// and no BLS signatures are send in epoch 0
if epoch.IsVoteExtensionProposal(ctx) {
// 1. extract the special tx containing the checkpoint
injectedCkpt, err := extractInjectedCheckpoint(req.Txs)
injectedCkpt, err := h.ExtractInjectedCheckpoint(req.Txs)
if err != nil {
h.logger.Error(
"processProposal: failed to extract injected checkpoint from the tx set", "err", err)
Expand Down Expand Up @@ -345,7 +358,7 @@ func (h *ProposalHandler) PreBlocker() sdk.PreBlocker {
}

// 1. extract the special tx containing BLS sigs
injectedCkpt, err := extractInjectedCheckpoint(req.Txs)
injectedCkpt, err := h.ExtractInjectedCheckpoint(req.Txs)
if err != nil {
return res, fmt.Errorf(
"preblocker: failed to extract injected checkpoint from the tx set: %w", err)
Expand All @@ -360,24 +373,37 @@ func (h *ProposalHandler) PreBlocker() sdk.PreBlocker {
}
}

// extractInjectedCheckpoint extracts the injected checkpoint from the tx set
func extractInjectedCheckpoint(txs [][]byte) (*ckpttypes.InjectedCheckpoint, error) {
func (h *ProposalHandler) buildInjectedTxBytes(injectedCkpt *ckpttypes.MsgInjectedCheckpoint) ([]byte, error) {
if err := h.txBuilder.SetMsgs(injectedCkpt); err != nil {
return nil, err
}

return h.txEncoder(h.txBuilder.GetTx())
}

// ExtractInjectedCheckpoint extracts the injected checkpoint from the tx set
func (h *ProposalHandler) ExtractInjectedCheckpoint(txs [][]byte) (*ckpttypes.MsgInjectedCheckpoint, error) {
if len(txs) < defaultInjectedTxIndex+1 {
return nil, fmt.Errorf("the tx set does not contain the injected tx")
}

injectedTx := txs[defaultInjectedTxIndex]
injectedTxBytes := txs[defaultInjectedTxIndex]

if len(injectedTx) == 0 {
if len(injectedTxBytes) == 0 {
return nil, fmt.Errorf("the injected vote extensions tx is empty")
}

var injectedCkpt ckpttypes.InjectedCheckpoint
if err := injectedCkpt.Unmarshal(injectedTx); err != nil {
injectedTx, err := h.txDecoder(injectedTxBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode injected vote extension tx: %w", err)
}
msgs := injectedTx.GetMsgs()
if len(msgs) != 1 {
return nil, fmt.Errorf("injected tx must have exact one message, got %d", len(msgs))
}
injectedCkpt := msgs[0].(*ckpttypes.MsgInjectedCheckpoint)

return &injectedCkpt, nil
return injectedCkpt, nil
}

// removeInjectedTx removes the injected tx from the tx set
Expand Down
14 changes: 11 additions & 3 deletions x/checkpointing/proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ import (
cbftt "github.com/cometbft/cometbft/abci/types"
cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto"
tendermintTypes "github.com/cometbft/cometbft/proto/tendermint/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/mempool"
protoio "github.com/cosmos/gogoproto/io"
"github.com/cosmos/gogoproto/proto"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"

appparams "github.com/babylonlabs-io/babylon/app/params"
"github.com/babylonlabs-io/babylon/crypto/bls12381"
"github.com/babylonlabs-io/babylon/testutil/datagen"
"github.com/babylonlabs-io/babylon/testutil/helper"
Expand Down Expand Up @@ -484,11 +487,17 @@ func TestPrepareProposalAtVoteExtensionHeight(t *testing.T) {
ek.EXPECT().GetTotalVotingPower(gomock.Any(), ec.Epoch.EpochNumber).Return(scenario.TotalPower).AnyTimes()
ek.EXPECT().GetValidatorSet(gomock.Any(), ec.Epoch.EpochNumber).Return(et.NewSortedValidatorSet(ToValidatorSet(scenario.ValidatorSet))).AnyTimes()

logger := log.NewTestLogger(t)
db := dbm.NewMemDB()
name := t.Name()
encCfg := appparams.DefaultEncodingConfig()
bApp := baseapp.NewBaseApp(name, logger, db, encCfg.TxConfig.TxDecoder(), baseapp.SetChainID("chain-test"))
h := checkpointing.NewProposalHandler(
log.NewNopLogger(),
ek,
mem,
nil,
bApp,
encCfg,
)

commitInfo, _, cometInfo := helper.ExtendedCommitToLastCommit(cbftt.ExtendedCommitInfo{Round: 0, Votes: scenario.Extensions})
Expand All @@ -502,8 +511,7 @@ func TestPrepareProposalAtVoteExtensionHeight(t *testing.T) {
} else {
require.NoError(t, err)
require.Len(t, prop.Txs, 1)
var checkpoint checkpointingtypes.InjectedCheckpoint
err := checkpoint.Unmarshal(prop.Txs[0])
checkpoint, err := h.ExtractInjectedCheckpoint(prop.Txs)
require.NoError(t, err)
err = verifyCheckpoint(scenario.ValidatorSet, checkpoint.Ckpt.Ckpt)
require.NoError(t, err)
Expand Down
Loading

0 comments on commit 0831dc6

Please sign in to comment.