Skip to content

Commit

Permalink
fix(liveness slash): do not schedule for next block (#1284)
Browse files Browse the repository at this point in the history
  • Loading branch information
danwt authored Oct 6, 2024
1 parent e00ef66 commit 90a8791
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 17 deletions.
17 changes: 9 additions & 8 deletions x/rollapp/keeper/liveness.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,27 @@ See ADR for more info https://www.notion.so/dymension/ADR-x-Sequencer-Liveness-S

// NextSlashOrJailHeight returns the next height on the HUB to slash or jail the rollapp
// It will respect all parameters passed in.
// Assumes that if current hub height is already a slash height, then to schedule for the next one.
func NextSlashOrJailHeight(
blocksSlashNoUpdate uint64, // time until first slash if not updating
blocksSlashInterval uint64, // gap between slash if still not updating
blocksJail uint64, // time until jail if not updating
heightHub int64, // current hub height
heightHub int64, // current height on the hub
heightLastRollappUpdate int64, // when was the rollapp last updated
) (
heightEvent int64, // hub height to schedule event
isJail bool, // is it a jail event? (false -> slash)
) {
// how long has the rollapp been down already?
// how long has the rollapp been down ?
down := uint64(heightHub - heightLastRollappUpdate)
// when should we schedule the next slash/jail, in terms of down time duration?
targetInterval := blocksSlashNoUpdate
if blocksSlashNoUpdate < down {
interval := blocksSlashNoUpdate
if blocksSlashNoUpdate <= down {
// round up to next slash interval
targetInterval += ((down-blocksSlashNoUpdate)/blocksSlashInterval + 1) * blocksSlashInterval
interval += ((down-blocksSlashNoUpdate)/blocksSlashInterval + 1) * blocksSlashInterval
}
heightEvent = heightLastRollappUpdate + int64(targetInterval)
isJail = blocksJail <= targetInterval
heightEvent = heightLastRollappUpdate + int64(interval)
isJail = blocksJail <= interval
return
}

Expand All @@ -49,6 +50,7 @@ func (k Keeper) CheckLiveness(ctx sdk.Context) {
return k.HandleLivenessEvent(ctx, e)
})
if err != nil {
// We intentionally do not reschedule the event. It's not MVP. Also, if it failed once, why would it succeed second time?
k.Logger(ctx).Error(
"Check liveness event",
"event", e,
Expand Down Expand Up @@ -102,7 +104,6 @@ func (k Keeper) ScheduleLivenessEvent(ctx sdk.Context, ra *types.Rollapp) {
ctx.BlockHeight(),
ra.LastStateUpdateHeight,
)
nextH = max(nextH, ctx.BlockHeight()+1)
ra.LivenessEventHeight = nextH
k.PutLivenessEvent(ctx, types.LivenessEvent{
RollappId: ra.RollappId,
Expand Down
43 changes: 43 additions & 0 deletions x/rollapp/keeper/liveness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,49 @@ import (
"github.com/dymensionxyz/dymension/v3/x/rollapp/types"
)

func TestLivenessArithmetic(t *testing.T) {
t.Run("simple case", func(t *testing.T) {
hEvent, _ := keeper.NextSlashOrJailHeight(
8,
4,
1000,
0,
0,
)
require.Equal(t, 8, int(hEvent))
})
t.Run("almost at the interval", func(t *testing.T) {
hEvent, _ := keeper.NextSlashOrJailHeight(
8,
4,
1000,
7,
0,
)
require.Equal(t, 8, int(hEvent))
})
t.Run("do not schedule for next height", func(t *testing.T) {
hEvent, _ := keeper.NextSlashOrJailHeight(
8,
4,
1000,
8,
0,
)
require.Equal(t, 12, int(hEvent))
})
t.Run("do not schedule for next height", func(t *testing.T) {
hEvent, _ := keeper.NextSlashOrJailHeight(
8,
4,
1000,
12,
0,
)
require.Equal(t, 16, int(hEvent))
})
}

// Storage and query operations work for the event queue
func TestLivenessEventsStorage(t *testing.T) {
_ = flag.Set("rapid.checks", "50")
Expand Down
18 changes: 9 additions & 9 deletions x/rollapp/keeper/msg_server_update_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState)
return nil, errorsmod.Wrap(gerrc.ErrFailedPrecondition, "rollapp is frozen")
}

// call the before-update-state hook
// currently used by `x/sequencer` to:
// 1. validate the state update submitter
// 2. complete the rotation of the proposer if needed
err := k.hooks.BeforeUpdateState(ctx, msg.Creator, msg.RollappId, msg.Last)
if err != nil {
return nil, errorsmod.Wrap(err, "before update state")
}

// verify the DRS version is not vulnerable
if k.IsDRSVersionVulnerable(ctx, msg.DrsVersion) {
// the rollapp is not marked as vulnerable yet, mark it now
Expand All @@ -38,15 +47,6 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState)
return &types.MsgUpdateStateResponse{}, nil
}

// call the before-update-state hook
// currently used by `x/sequencer` to:
// 1. validate the state update submitter
// 2. complete the rotation of the proposer if needed
err := k.hooks.BeforeUpdateState(ctx, msg.Creator, msg.RollappId, msg.Last)
if err != nil {
return nil, err
}

// retrieve last updating index
var newIndex, lastIndex uint64
latestStateInfoIndex, found := k.GetLatestStateInfoIndex(ctx, msg.RollappId)
Expand Down

0 comments on commit 90a8791

Please sign in to comment.