Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1,286 changes: 104 additions & 1,182 deletions api/heimdallv2/bor/tx.pulsar.go

Large diffs are not rendered by default.

41 changes: 0 additions & 41 deletions api/heimdallv2/bor/tx_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 1 addition & 8 deletions app/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5089,7 +5089,7 @@ func TestABCI_StressTestWith100Blocks(t *testing.T) {
var msg sdk.Msg

// Alternate between different message types
switch (blockHeight + int64(i)) % 5 {
switch (blockHeight + int64(i)) % 4 {
case 0:
msg = &checkpointTypes.MsgCheckpoint{
Proposer: priv.PubKey().Address().String(),
Expand Down Expand Up @@ -5128,13 +5128,6 @@ func TestABCI_StressTestWith100Blocks(t *testing.T) {
LogIndex: uint64(blockHeight*10 + int64(i)),
BlockNumber: uint64(blockHeight),
}
case 4:
msg = &borTypes.MsgBackfillSpans{
Proposer: priv.PubKey().Address().String(),
ChainId: "1",
LatestSpanId: uint64(blockHeight),
LatestBorSpanId: uint64(blockHeight + 1),
}
}

txBytes, err := buildSignedTx(msg, ctx, priv, app)
Expand Down
2 changes: 0 additions & 2 deletions metrics/api/bor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const (

ProposeSpanMethod = "ProposeSpan"
BorUpdateParamsMethod = "UpdateParams"
BackfillSpansMethod = "BackfillSpans"
VoteProducersMethod = "VoteProducers"
ProducerDowntimeMethod = "ProducerDowntime"

Expand All @@ -30,6 +29,5 @@ const (
// Post message handler methods.

PostHandleMsgSpanMethod = "PostHandleMsgSpan"
PostHandleMsgBackfillSpansMethod = "PostHandleMsgBackfillSpans"
PostHandleMsgSetProducerDowntimeMethod = "PostHandleMsgSetProducerDowntime"
)
20 changes: 0 additions & 20 deletions proto/heimdallv2/bor/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@
option (cosmos.msg.v1.service) = true;
// ProposeSpan defines a method for proposing a new bor span.
rpc ProposeSpan(MsgProposeSpan) returns (MsgProposeSpanResponse);
// UpdateParams defines a method to update the bor module parameters.
// Only the governance authority can execute this.
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);

Check failure on line 19 in proto/heimdallv2/bor/tx.proto

View check run for this annotation

Claude / Claude Code Review

MsgBackfillSpans codec removal may break replay of historical blocks

Removing the `BackfillSpans` rpc from `tx.proto` also removes `MsgBackfillSpans` from the InterfaceRegistry (via `msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)` in `x/bor/types/codec.go`), so any historical block containing a `/heimdallv2.bor.MsgBackfillSpans` tx will fail `TxDecode` post-upgrade and halt sync/replay. The rollout note's "never accepted a *state-changing* BackfillSpans tx" qualifier is the wrong invariant for replay safety — pre-PR the msg-server validated inputs
Comment thread
lucca30 marked this conversation as resolved.
// BackfillSpans defines a method to backfill missing spans.
// This is used during chain recovery or when spans need to be reconstructed.
rpc BackfillSpans(MsgBackfillSpans) returns (MsgBackfillSpansResponse);
// VoteProducers defines a method for validators to submit their producer
// votes.
rpc VoteProducers(MsgVoteProducers) returns (MsgVoteProducersResponse);
Expand Down Expand Up @@ -67,23 +64,6 @@
// MsgUpdateParamsResponse defines the response for MsgUpdateParams.
message MsgUpdateParamsResponse {}

// MsgBackfillSpans defines the message for backfilling missing spans.
message MsgBackfillSpans {
option (amino.name) = "heimdallv2/bor/MsgBackfillSpans";
option (cosmos.msg.v1.signer) = "proposer";
// Address of the validator proposing the backfill.
string proposer = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// Chain ID of the Bor chain.
string chain_id = 2;
// Latest span ID known to Heimdall.
uint64 latest_span_id = 3;
// Latest span ID on the Bor chain.
uint64 latest_bor_span_id = 4;
}

// MsgBackfillSpansResponse defines the response for MsgBackfillSpans.
message MsgBackfillSpansResponse {}

// MsgVoteProducers defines the message for voting on producers.
message MsgVoteProducers {
option (cosmos.msg.v1.signer) = "voter";
Expand Down
89 changes: 0 additions & 89 deletions x/bor/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,95 +170,6 @@ func (srv msgServer) VoteProducers(ctx context.Context, msg *types.MsgVoteProduc
return &types.MsgVoteProducersResponse{}, nil
}

func (srv msgServer) BackfillSpans(ctx context.Context, msg *types.MsgBackfillSpans) (*types.MsgBackfillSpansResponse, error) {
var err error
start := time.Now()
defer recordBorTransactionMetric(api.BackfillSpansMethod, start, &err)

logger := srv.Logger(ctx)

logger.Debug(helper.LogValidatingExternalCall("MsgSpanBackfill"),
"proposer", msg.Proposer,
"latestSpanId", msg.LatestSpanId,
"latestBorSpanId", msg.LatestBorSpanId,
"chainId", msg.ChainId,
)

_, err = sdk.ValAddressFromHex(msg.Proposer)
if err != nil {
logger.Error(heimdallTypes.ErrMsgInvalidProposerAddress, "error", err)
return nil, errors.Wrapf(err, heimdallTypes.ErrMsgInvalidProposerAddress)
}

chainParams, err := srv.ck.GetParams(ctx)
if err != nil {
logger.Error("Failed to get chain params", "error", err)
return nil, errors.Wrapf(err, "failed to get chain params")
}

if !helper.ValidateChainID(msg.ChainId, chainParams.ChainParams.BorChainId, logger, "bor") {
return nil, types.ErrInvalidChainID
}

latestSpan, err := srv.GetLastSpan(ctx)
if err != nil {
logger.Error("Failed to get latest span", "error", err)
return nil, errors.Wrapf(err, "failed to get latest span")
}

if msg.LatestSpanId != latestSpan.Id && msg.LatestSpanId != latestSpan.Id-1 {
logger.Error("Invalid latest span id", "expected",
fmt.Sprintf("%d or %d", latestSpan.Id, latestSpan.Id-1), "got", msg.LatestSpanId)
return nil, types.ErrInvalidSpan
}

if msg.LatestBorSpanId <= msg.LatestSpanId {
logger.Error("Invalid bor span id, expected greater than latest span id",
"latestSpanId", latestSpan.Id,
"latestBorSpanId", msg.LatestBorSpanId,
)
return nil, types.ErrInvalidLastBorSpanID
}

latestMilestone, err := srv.mk.GetLastMilestone(ctx)
if err != nil {
logger.Error("Failed to get latest milestone", "error", err)
return nil, errors.Wrapf(err, "failed to get latest milestone")
}

if latestMilestone == nil {
logger.Error("Latest milestone is nil")
return nil, types.ErrLatestMilestoneNotFound
}

borLastUsedSpan, err := srv.GetSpan(ctx, msg.LatestSpanId)
if err != nil {
logger.Error("Failed to get last used bor span", "error", err)
return nil, errors.Wrapf(err, "failed to get last used bor span")
}

borSpanId, err := types.CalcCurrentBorSpanId(latestMilestone.EndBlock, &borLastUsedSpan)
if err != nil {
logger.Error("Failed to calculate bor span id", "error", err)
return nil, errors.Wrapf(err, "failed to calculate bor span id")
}

if borSpanId != msg.LatestBorSpanId {
logger.Error(
"bor span id mismatch",
"calculatedBorSpanId", borSpanId,
"msgLatestBorSpanId", msg.LatestBorSpanId,
"latestMilestoneEndBlock", latestMilestone.EndBlock,
"latestSpanStartBlock", latestSpan.StartBlock,
"latestSpanEndBlock", latestSpan.EndBlock,
"latestSpanId", latestSpan.Id,
)
return nil, types.ErrInvalidSpan
}

return &types.MsgBackfillSpansResponse{}, nil
}

func (srv msgServer) SetProducerDowntime(ctx context.Context, msg *types.MsgSetProducerDowntime) (*types.MsgSetProducerDowntimeResponse, error) {
var err error
start := time.Now()
Expand Down
89 changes: 0 additions & 89 deletions x/bor/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/0xPolygon/heimdall-v2/helper"
"github.com/0xPolygon/heimdall-v2/x/bor/types"
chainmanagertypes "github.com/0xPolygon/heimdall-v2/x/chainmanager/types"
milestoneTypes "github.com/0xPolygon/heimdall-v2/x/milestone/types"
staketypes "github.com/0xPolygon/heimdall-v2/x/stake/types"
)

Expand Down Expand Up @@ -423,94 +422,6 @@ func (s *KeeperTestSuite) TestVoteProducers() {
}
}

func (s *KeeperTestSuite) TestBackfillSpans() {
require, ctx, borKeeper, milestoneKeeper, cmKeeper, msgServer := s.Require(), s.ctx, s.borKeeper, s.milestoneKeeper, s.chainManagerKeeper, s.msgServer

testChainParams := chainmanagertypes.DefaultParams()
testSpan := s.genTestSpans(1)[0]
err := borKeeper.AddNewSpan(ctx, testSpan)
require.NoError(err)

testcases := []struct {
name string
backfillSpans types.MsgBackfillSpans
expRes *types.MsgBackfillSpansResponse
expErr string
}{
{
name: "incorrect validator address",
backfillSpans: types.MsgBackfillSpans{
Proposer: "ValidatorAddress",
ChainId: testChainParams.ChainParams.BorChainId,
LatestSpanId: 1,
LatestBorSpanId: 7,
},
expRes: nil,
expErr: "invalid proposer address",
},
{
name: "incorrect chain id",
backfillSpans: types.MsgBackfillSpans{
Proposer: common.HexToAddress("someProposer").String(),
ChainId: "invalidChainId",
LatestSpanId: 1,
LatestBorSpanId: 7,
},
expRes: nil,
expErr: "invalid bor chain id",
},
{
name: "invalid last heimdall span id",
backfillSpans: types.MsgBackfillSpans{
Proposer: common.HexToAddress("someProposer").String(),
ChainId: testChainParams.ChainParams.BorChainId,
LatestSpanId: 2,
LatestBorSpanId: 7,
},
expRes: nil,
expErr: "invalid span",
},
{
name: "invalid last bor span id",
backfillSpans: types.MsgBackfillSpans{
Proposer: common.HexToAddress("someProposer").String(),
ChainId: testChainParams.ChainParams.BorChainId,
LatestSpanId: 1,
LatestBorSpanId: 0,
},
expErr: "invalid last bor span id",
},
{
name: "mismatch between calculated and provided last span id",
backfillSpans: types.MsgBackfillSpans{
Proposer: common.HexToAddress("someProposer").String(),
ChainId: testChainParams.ChainParams.BorChainId,
LatestSpanId: 1,
LatestBorSpanId: 3,
},
expRes: nil,
expErr: "invalid span",
},
}

cmKeeper.EXPECT().GetParams(ctx).Return(testChainParams, nil).AnyTimes()
milestoneKeeper.EXPECT().GetLastMilestone(ctx).Return(&milestoneTypes.Milestone{
EndBlock: 1000,
}, nil).AnyTimes()

for _, tc := range testcases {
s.T().Run(tc.name, func(t *testing.T) {
res, err := msgServer.BackfillSpans(ctx, &tc.backfillSpans)
require.Equal(tc.expRes, res)
if tc.expErr == "" {
require.NoError(err)
} else {
require.ErrorContains(err, tc.expErr)
}
})
}
}

func (s *KeeperTestSuite) TestCanVoteProducers() {
require, ctx, borKeeper := s.Require(), s.ctx, s.borKeeper

Expand Down
Loading
Loading