Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions api/heimdallv2/bor/tx.pulsar.go

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

12 changes: 10 additions & 2 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.

1 change: 0 additions & 1 deletion metrics/api/bor.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,5 @@ const (
// Post message handler methods.

PostHandleMsgSpanMethod = "PostHandleMsgSpan"
PostHandleMsgBackfillSpansMethod = "PostHandleMsgBackfillSpans"
PostHandleMsgSetProducerDowntimeMethod = "PostHandleMsgSetProducerDowntime"
)
11 changes: 10 additions & 1 deletion proto/heimdallv2/bor/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ service Msg {
// Only the governance authority can execute this.
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
// BackfillSpans defines a method to backfill missing spans.
// This is used during chain recovery or when spans need to be reconstructed.
// The side handler / post handler / dispatch are deliberately not registered
// (see x/bor/keeper/side_msg_server.go SideTxHandler / PostTxHandler) so
// this msg consumes no side-tx response slot. The msg-server handler is
// retained as input-validation only (no state writes) so historical blocks
// containing this msg replay with the same ExecTxResult shape.
rpc BackfillSpans(MsgBackfillSpans) returns (MsgBackfillSpansResponse);
// VoteProducers defines a method for validators to submit their producer
// votes.
Expand Down Expand Up @@ -68,6 +72,11 @@ message MsgUpdateParams {
message MsgUpdateParamsResponse {}

// MsgBackfillSpans defines the message for backfilling missing spans.
// Kept so historical blocks containing a MsgBackfillSpans tx (2 known on
// Amoy at heights 8790181 and 8790209) replay with matching ExecTxResult.
// The side handler / post handler / dispatch were removed to close the
// side-tx response-slot censorship vector; the msg-server handler is
// validation-only (no state writes).
message MsgBackfillSpans {
option (amino.name) = "heimdallv2/bor/MsgBackfillSpans";
option (cosmos.msg.v1.signer) = "proposer";
Expand Down
86 changes: 1 addition & 85 deletions x/bor/keeper/side_msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ const (

var (
SpanProposeMsgTypeURL = sdk.MsgTypeURL(&types.MsgProposeSpan{})
FillMissingSpansMsgTypeURL = sdk.MsgTypeURL(&types.MsgBackfillSpans{})
SetProducerDowntimeMsgTypeURL = sdk.MsgTypeURL(&types.MsgSetProducerDowntime{})
)
Comment thread
lucca30 marked this conversation as resolved.

Expand All @@ -50,8 +49,6 @@ func (srv sideMsgServer) SideTxHandler(methodName string) sidetxs.SideTxHandler
switch methodName {
case SpanProposeMsgTypeURL:
return srv.SideHandleMsgSpan
case FillMissingSpansMsgTypeURL:
return srv.SideHandleMsgBackfillSpans
case SetProducerDowntimeMsgTypeURL:
return srv.SideHandleSetProducerDowntime
default:
Expand Down Expand Up @@ -197,7 +194,7 @@ func (srv sideMsgServer) SideHandleMsgSpan(ctx sdk.Context, msgI sdk.Msg) sidetx
return sidetxs.Vote_VOTE_NO
}

// If we are past the end of the last span, we need to backfill before proposing a new span
// Reject: the proposed span starts at or before the last committed block, so it is already in the past.
if msg.StartBlock <= maxBlockNumber {
logger.Error("Span is already in the past",
"currentBlock", currentBlock,
Expand Down Expand Up @@ -355,17 +352,11 @@ func (srv sideMsgServer) SideHandleSetProducerDowntime(ctx sdk.Context, msgI sdk
return sidetxs.Vote_VOTE_YES
}

func (srv sideMsgServer) SideHandleMsgBackfillSpans(_ sdk.Context, _ sdk.Msg) sidetxs.Vote {
return sidetxs.Vote_VOTE_NO
}

// PostTxHandler returns a side handler for span type messages.
func (srv sideMsgServer) PostTxHandler(methodName string) sidetxs.PostTxHandler {
switch methodName {
case SpanProposeMsgTypeURL:
return srv.PostHandleMsgSpan
case FillMissingSpansMsgTypeURL:
return srv.PostHandleMsgBackfillSpans
case SetProducerDowntimeMsgTypeURL:
return srv.PostHandleSetProducerDowntime
default:
Expand Down Expand Up @@ -449,81 +440,6 @@ func (srv sideMsgServer) PostHandleMsgSpan(ctx sdk.Context, msgI sdk.Msg, sideTx
return nil
}

func (srv sideMsgServer) PostHandleMsgBackfillSpans(ctx sdk.Context, msgI sdk.Msg, sideTxResult sidetxs.Vote) error {
var err error
start := time.Now()
defer recordBorMetric(api.PostHandleMsgBackfillSpansMethod, api.PostType, start, &err)

logger := srv.k.Logger(ctx)

msg, ok := msgI.(*types.MsgBackfillSpans)
if !ok {
err = errors.New("MsgBackfillSpans type mismatch")
logger.Error(err.Error(), msgTypeReceivedLog, msg)
return err
}

if helper.IsRio(msg.LatestSpanId) {
logger.Debug("Skipping backfill spans msg since span id is greater than rio height", spanIdLog, msg.LatestSpanId, rioHeightStr, helper.GetRioHeight())
return nil
}

if sideTxResult != sidetxs.Vote_VOTE_YES {
logger.Debug("Skipping new span since side-tx didn't get yes votes")
return errors.New(sidTxNoYesVotesLog)
}

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

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

latestSpan, err := srv.k.GetSpan(ctx, msg.LatestSpanId)
if err != nil {
logger.Error("Failed to get latest span", "error", err)
return err
}

borSpans := types.GenerateBorCommittedSpans(latestMilestone.EndBlock, &latestSpan)
spansOverlap := 0
for i := range borSpans {
if _, err := srv.k.GetSpan(ctx, borSpans[i].Id); err == nil {
spansOverlap++
}
if spansOverlap > 1 {
logger.Error("More than one span overlap detected", spanIdLog, borSpans[i].Id)
return fmt.Errorf("more than one span overlap detected for span id: %d", borSpans[i].Id)
}
if err = srv.k.AddNewSpan(ctx, &borSpans[i]); err != nil {
logger.Error("Unable to store spans", "error", err)
return err
}
}

txBytes := ctx.TxBytes()
hash := cmttypes.Tx(txBytes).Hash()

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeProposeSpan,
sdk.NewAttribute(sdk.AttributeKeyAction, msg.Type()),
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(heimdallTypes.AttributeKeyTxHash, common.BytesToHash(hash).Hex()),
sdk.NewAttribute(heimdallTypes.AttributeKeySideTxResult, sideTxResult.String()),
sdk.NewAttribute(types.AttributesKeyLatestSpanId, strconv.FormatUint(msg.LatestSpanId, 10)),
sdk.NewAttribute(types.AttributesKeyLatestBorSpanId, strconv.FormatUint(borSpans[0].Id, 10)),
),
})

return nil
}

func (srv sideMsgServer) PostHandleSetProducerDowntime(ctx sdk.Context, msgI sdk.Msg, sideTxResult sidetxs.Vote) error {
var err error
start := time.Now()
Expand Down
11 changes: 4 additions & 7 deletions x/bor/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ package types

// bor module event types
const (
EventTypeProposeSpan = "propose-span"
EventTypeBackfillSpans = "backfill-spans"
EventTypeProposeSpan = "propose-span"

AttributeKeySpanID = "span-id"
AttributeKeySpanStartBlock = "start-block"
AttributeKeySpanEndBlock = "end-block"
AttributesKeyLatestSpanId = "latest-span-id"
AttributesKeyLatestBorSpanId = "latest-bor-span-id"
AttributeKeySpanID = "span-id"
AttributeKeySpanStartBlock = "start-block"
AttributeKeySpanEndBlock = "end-block"

AttributeValueCategory = ModuleName
)
86 changes: 1 addition & 85 deletions x/bor/types/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ func TestEventTypeProposeSpan(t *testing.T) {
require.NotEmpty(t, types.EventTypeProposeSpan)
}

func TestEventTypeBackfillSpans(t *testing.T) {
require.Equal(t, "backfill-spans", types.EventTypeBackfillSpans)
require.NotEmpty(t, types.EventTypeBackfillSpans)
}

func TestAttributeKeySpanID(t *testing.T) {
require.Equal(t, "span-id", types.AttributeKeySpanID)
require.NotEmpty(t, types.AttributeKeySpanID)
Expand All @@ -33,40 +28,17 @@ func TestAttributeKeySpanEndBlock(t *testing.T) {
require.NotEmpty(t, types.AttributeKeySpanEndBlock)
}

func TestAttributesKeyLatestSpanId(t *testing.T) {
require.Equal(t, "latest-span-id", types.AttributesKeyLatestSpanId)
require.NotEmpty(t, types.AttributesKeyLatestSpanId)
}

func TestAttributesKeyLatestBorSpanId(t *testing.T) {
require.Equal(t, "latest-bor-span-id", types.AttributesKeyLatestBorSpanId)
require.NotEmpty(t, types.AttributesKeyLatestBorSpanId)
}

func TestAttributeValueCategory(t *testing.T) {
require.Equal(t, types.ModuleName, types.AttributeValueCategory)
require.Equal(t, "bor", types.AttributeValueCategory)
}

func TestEventTypesUniqueness(t *testing.T) {
// Test that event types are unique
eventTypes := []string{
types.EventTypeProposeSpan,
types.EventTypeBackfillSpans,
}

require.Len(t, eventTypes, 2)
require.NotEqual(t, eventTypes[0], eventTypes[1])
}

func TestAttributeKeysUniqueness(t *testing.T) {
// Test that all attribute keys are unique
attributeKeys := []string{
types.AttributeKeySpanID,
types.AttributeKeySpanStartBlock,
types.AttributeKeySpanEndBlock,
types.AttributesKeyLatestSpanId,
types.AttributesKeyLatestBorSpanId,
}

// Check all keys are distinct
Expand All @@ -78,27 +50,12 @@ func TestAttributeKeysUniqueness(t *testing.T) {
}
}

func TestEventTypesFormat(t *testing.T) {
// Test that event types follow the kebab-case convention
eventTypes := []string{
types.EventTypeProposeSpan,
types.EventTypeBackfillSpans,
}

for _, eventType := range eventTypes {
require.Contains(t, eventType, "-",
"Event type should use kebab-case: %s", eventType)
}
}

func TestAttributeKeysFormat(t *testing.T) {
// Test that attribute keys follow kebab-case convention
attributeKeys := []string{
types.AttributeKeySpanID,
types.AttributeKeySpanStartBlock,
types.AttributeKeySpanEndBlock,
types.AttributesKeyLatestSpanId,
types.AttributesKeyLatestBorSpanId,
}

for _, key := range attributeKeys {
Expand All @@ -108,23 +65,11 @@ func TestAttributeKeysFormat(t *testing.T) {
}

func TestEventTypesContainSpan(t *testing.T) {
// Test that span-related events contain "span" in their name
require.Contains(t, types.EventTypeProposeSpan, "span")
require.Contains(t, types.EventTypeBackfillSpans, "span")
}

func TestSpanAttributesContainSpan(t *testing.T) {
// Test that span-id attributes contain "span" in their name
spanAttributes := []string{
types.AttributeKeySpanID,
types.AttributesKeyLatestSpanId,
types.AttributesKeyLatestBorSpanId,
}

for _, attr := range spanAttributes {
require.Contains(t, attr, "span",
"Span attribute should contain 'span': %s", attr)
}
require.Contains(t, types.AttributeKeySpanID, "span")
}

func TestBlockAttributes(t *testing.T) {
Expand All @@ -133,34 +78,11 @@ func TestBlockAttributes(t *testing.T) {
require.Contains(t, types.AttributeKeySpanEndBlock, "block")
}

func TestLatestAttributes(t *testing.T) {
// Test latest-related attributes
latestAttributes := []string{
types.AttributesKeyLatestSpanId,
types.AttributesKeyLatestBorSpanId,
}

for _, attr := range latestAttributes {
require.Contains(t, attr, "latest",
"Latest attribute should contain 'latest': %s", attr)
}
}

func TestBorAttributes(t *testing.T) {
// Test bor-specific attributes
require.Contains(t, types.AttributesKeyLatestBorSpanId, "bor")
}

func TestProposeSpanEventType(t *testing.T) {
require.Contains(t, types.EventTypeProposeSpan, "propose")
require.Contains(t, types.EventTypeProposeSpan, "span")
}

func TestBackfillSpansEventType(t *testing.T) {
require.Contains(t, types.EventTypeBackfillSpans, "backfill")
require.Contains(t, types.EventTypeBackfillSpans, "spans")
}

func TestSpanIDAttribute(t *testing.T) {
require.Contains(t, types.AttributeKeySpanID, "span")
require.Contains(t, types.AttributeKeySpanID, "id")
Expand All @@ -180,12 +102,9 @@ func TestAllEventConstantsNotEmpty(t *testing.T) {
// Test that all event-related constants are not empty
constants := []string{
types.EventTypeProposeSpan,
types.EventTypeBackfillSpans,
types.AttributeKeySpanID,
types.AttributeKeySpanStartBlock,
types.AttributeKeySpanEndBlock,
types.AttributesKeyLatestSpanId,
types.AttributesKeyLatestBorSpanId,
types.AttributeValueCategory,
}

Expand All @@ -205,12 +124,9 @@ func TestEventConstantsAreLowercase(t *testing.T) {
// Test that constants follow the lowercase-kebab-case convention
constants := []string{
types.EventTypeProposeSpan,
types.EventTypeBackfillSpans,
types.AttributeKeySpanID,
types.AttributeKeySpanStartBlock,
types.AttributeKeySpanEndBlock,
types.AttributesKeyLatestSpanId,
types.AttributesKeyLatestBorSpanId,
types.AttributeValueCategory,
}

Expand Down
Loading
Loading