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
165 changes: 113 additions & 52 deletions api/heimdallv2/bor/tx.pulsar.go

Large diffs are not rendered by default.

21 changes: 17 additions & 4 deletions app/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,19 @@ func (app *HeimdallApp) NewPrepareProposalHandler() sdk.PrepareProposalHandler {
break
}
}
if _, ok := msg.(*borTypes.MsgSetProducerDowntime); ok {
if downtimeMsg, ok := msg.(*borTypes.MsgSetProducerDowntime); ok {
if err := app.BorKeeper.CanSetProducerDowntime(sdk.UnwrapSDKContext(ctx)); err != nil {
logger.Info("Skipping MsgSetProducerDowntime in PrepareProposal", "error", err)
shouldSkip = true
break
}
if downtimeMsg.TargetProducerId != borTypes.RoundRobinDefault {
if err := app.BorKeeper.CanUseTargetProducer(sdk.UnwrapSDKContext(ctx)); err != nil {
logger.Info("Skipping MsgSetProducerDowntime with TargetProducerId in PrepareProposal", "error", err)
shouldSkip = true
break
}
}
}
}

Expand Down Expand Up @@ -195,11 +202,17 @@ func (app *HeimdallApp) NewProcessProposalHandler() sdk.ProcessProposalHandler {
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
}
if _, ok := msg.(*borTypes.MsgSetProducerDowntime); ok {
if downtimeMsg, ok := msg.(*borTypes.MsgSetProducerDowntime); ok {
if err := app.BorKeeper.CanSetProducerDowntime(sdk.UnwrapSDKContext(ctx)); err != nil {
logger.Error("Rejecting proposal with invalid MsgSetProducerDowntime", "error", err)
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
if downtimeMsg.TargetProducerId != borTypes.RoundRobinDefault {
if err := app.BorKeeper.CanUseTargetProducer(sdk.UnwrapSDKContext(ctx)); err != nil {
logger.Error("Rejecting proposal with MsgSetProducerDowntime containing TargetProducerId before fork height", "error", err)
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
}
}
}

Expand Down Expand Up @@ -794,7 +807,7 @@ func (app *HeimdallApp) checkAndAddFutureSpan(ctx sdk.Context, majorityMilestone
return err
}

err = app.BorKeeper.AddNewVeBlopSpan(ctx, currentProducer, lastSpan.EndBlock+1, endBlock, lastSpan.BorChainId, supportingValidatorIDs, uint64(ctx.BlockHeight()))
err = app.BorKeeper.AddNewVeBlopSpan(ctx, currentProducer, lastSpan.EndBlock+1, endBlock, lastSpan.BorChainId, supportingValidatorIDs, uint64(ctx.BlockHeight()), borTypes.RoundRobinDefault)
if err != nil {
logger.Error("Error occurred while adding new veblop span", "error", err)
return err
Expand Down Expand Up @@ -902,7 +915,7 @@ func (app *HeimdallApp) checkAndRotateCurrentSpan(ctx sdk.Context) error {

delete(latestActiveProducer, currentProducer)

err = app.BorKeeper.AddNewVeBlopSpan(addSpanCtx, currentProducer, lastMilestone.EndBlock+1, endBlock, lastMilestone.BorChainId, latestActiveProducer, uint64(ctx.BlockHeight()))
err = app.BorKeeper.AddNewVeBlopSpan(addSpanCtx, currentProducer, lastMilestone.EndBlock+1, endBlock, lastMilestone.BorChainId, latestActiveProducer, uint64(ctx.BlockHeight()), borTypes.RoundRobinDefault)
if err != nil {
logger.Warn("Error occurred while adding new veblop span", "error", err)
} else {
Expand Down
11 changes: 10 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ func (app *HeimdallApp) CheckTx(req *abci.RequestCheckTx) (*abci.ResponseCheckTx
Log: err.Error(),
}, nil
}
} else if _, ok := msg.(*borTypes.MsgSetProducerDowntime); ok {
} else if downtimeMsg, ok := msg.(*borTypes.MsgSetProducerDowntime); ok {
// Create a context for validation
ctx := app.NewUncachedContext(true, cmtproto.Header{Height: app.LastBlockHeight() + 1})
if err := app.BorKeeper.CanSetProducerDowntime(ctx); err != nil {
Expand All @@ -519,6 +519,15 @@ func (app *HeimdallApp) CheckTx(req *abci.RequestCheckTx) (*abci.ResponseCheckTx
Log: err.Error(),
}, nil
}
if downtimeMsg.TargetProducerId != borTypes.RoundRobinDefault {
if err := app.BorKeeper.CanUseTargetProducer(ctx); err != nil {
app.Logger().Debug("Rejecting MsgSetProducerDowntime with TargetProducerId in CheckTx", "error", err)
return &abci.ResponseCheckTx{
Code: sdkerrors.ErrInvalidRequest.ABCICode(),
Log: err.Error(),
}, nil
}
}
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion helper/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ const (
DefaultMilestonePollInterval = 30 * time.Second

// Self-healing defaults

DefaultEnableSH = false
DefaultSHStateSyncedInterval = 3 * time.Hour
DefaultSHStakeUpdateInterval = 3 * time.Hour
Expand Down Expand Up @@ -238,6 +237,8 @@ var faultyMilestoneNumber int64 = 0

var producerDowntimeHeight int64 = 0

var targetProducerOverrideHeight int64 = 0

type ChainManagerAddressMigration struct {
PolTokenAddress string
RootChainAddress string
Expand Down Expand Up @@ -485,6 +486,7 @@ func InitHeimdallConfigWith(homeDir string, heimdallConfigFileFromFlag string) {
disableValSetCheckHeight = 25723063
initialHeight = 24404501
producerDowntimeHeight = 34966593
targetProducerOverrideHeight = 0 // TBD
case MumbaiChain:
milestoneDeletionHeight = 0
faultyMilestoneNumber = -1
Expand All @@ -494,6 +496,7 @@ func InitHeimdallConfigWith(homeDir string, heimdallConfigFileFromFlag string) {
disableValSetCheckHeight = 0
initialHeight = 0
producerDowntimeHeight = 0
targetProducerOverrideHeight = 0 // TBD
case AmoyChain:
milestoneDeletionHeight = 0
faultyMilestoneNumber = -1
Expand All @@ -503,6 +506,7 @@ func InitHeimdallConfigWith(homeDir string, heimdallConfigFileFromFlag string) {
disableValSetCheckHeight = 10618299
initialHeight = 8788501
producerDowntimeHeight = 20457139
targetProducerOverrideHeight = 0 // TBD
default:
milestoneDeletionHeight = 0
faultyMilestoneNumber = -1
Expand All @@ -512,6 +516,7 @@ func InitHeimdallConfigWith(homeDir string, heimdallConfigFileFromFlag string) {
disableValSetCheckHeight = 0
initialHeight = 0
producerDowntimeHeight = 0
targetProducerOverrideHeight = 0
}
}

Expand Down Expand Up @@ -692,6 +697,14 @@ func GetSetProducerDowntimeHeight() int64 {
return producerDowntimeHeight
}

func GetTargetProducerOverrideHeight() int64 {
return targetProducerOverrideHeight
}

func SetTargetProducerOverrideHeight(height int64) {
targetProducerOverrideHeight = height
}

func GetChainManagerAddressMigration(blockNum int64) (ChainManagerAddressMigration, bool) {
chainMigration := chainManagerAddressMigrations[conf.Custom.Chain]
if chainMigration == nil {
Expand Down
3 changes: 3 additions & 0 deletions proto/heimdallv2/bor/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ message MsgSetProducerDowntime {
// Block range during which the producer will be offline.
BlockRange downtime_range = 2
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
// Validator ID of the preferred producer to replace the downtime producer.
// 0 = round-robin (default).
uint64 target_producer_id = 3;
}

// MsgSetProducerDowntimeResponse defines the response for
Expand Down
1 change: 1 addition & 0 deletions x/bor/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ const (
FlagStartTimestampUTC = "start-timestamp-utc"
FlagEndTimestampUTC = "end-timestamp-utc"
FlagCalcOnly = "calc-only"
FlagTargetProducerID = "target-producer-id"
)
4 changes: 3 additions & 1 deletion x/bor/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ func NewProducerDowntimeCmd() *cobra.Command {
return nil
}

msg := types.NewMsgSetProducerDowntime(producerAddress, startBlock, endBlock)
targetProducerID := viper.GetUint64(FlagTargetProducerID)
msg := types.NewMsgSetProducerDowntime(producerAddress, startBlock, endBlock, targetProducerID)

return cli.BroadcastMsg(clientCtx, producerAddress, msg, logger)
},
Expand All @@ -217,6 +218,7 @@ func NewProducerDowntimeCmd() *cobra.Command {
cmd.Flags().Int(FlagStartTimestampUTC, 0, "--start-timestamp-utc=<start-timestamp-utc>")
cmd.Flags().Int(FlagEndTimestampUTC, 0, "--end-timestamp-utc=<end-timestamp-utc>")
cmd.Flags().Bool(FlagCalcOnly, false, "--calc-only=<true|false>")
cmd.Flags().Uint64(FlagTargetProducerID, 0, "--target-producer-id=<target-producer-id>")

if err := cmd.MarkFlagRequired(FlagProducerAddress); err != nil {
fmt.Printf("NewProducerDowntimeCmd | MarkFlagRequired | FlagProducerAddress Error: %v", err)
Expand Down
18 changes: 15 additions & 3 deletions x/bor/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,14 +596,26 @@ func (k Keeper) CanVoteProducers(ctx context.Context) error {
return nil
}

// CanSetProducerDowntime checks if the current height is after the setProducerDowntimeHeight
// CanSetProducerDowntime checks if the current height is after the setProducerDowntimeHeight.
func (k Keeper) CanSetProducerDowntime(ctx sdk.Context) error {
if uint64(ctx.BlockHeight()) < uint64(helper.GetSetProducerDowntimeHeight()) {
if ctx.BlockHeight() < helper.GetSetProducerDowntimeHeight() {
return fmt.Errorf("MsgSetProducerDowntime not allowed: block %d is before the setProducerDowntimeHeight %d",
ctx.BlockHeight(),
helper.GetSetProducerDowntimeHeight())
helper.GetSetProducerDowntimeHeight(),
)
}
return nil
}

// CanUseTargetProducer checks if the current height is after the targetProducerOverrideHeight,
// which gates the use of a non-default TargetProducerId in MsgSetProducerDowntime.
func (k Keeper) CanUseTargetProducer(ctx sdk.Context) error {
if ctx.BlockHeight() < helper.GetTargetProducerOverrideHeight() {
return fmt.Errorf("MsgSetProducerDowntime with TargetProducerId not allowed: block %d is before the targetProducerOverrideHeight %d",
ctx.BlockHeight(),
helper.GetTargetProducerOverrideHeight(),
)
}
return nil
}

Expand Down
22 changes: 22 additions & 0 deletions x/bor/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@
return &types.MsgBackfillSpansResponse{}, nil
}

func (srv msgServer) SetProducerDowntime(ctx context.Context, msg *types.MsgSetProducerDowntime) (*types.MsgSetProducerDowntimeResponse, error) {

Check failure on line 262 in x/bor/keeper/msg_server.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 27 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=0xPolygon_heimdall-v2&issues=AZ0fjLruCIaBrby_9LDq&open=AZ0fjLruCIaBrby_9LDq&pullRequest=567
var err error
start := time.Now()
defer recordBorTransactionMetric(api.ProducerDowntimeMethod, start, &err)
Expand Down Expand Up @@ -325,6 +325,28 @@
return nil, fmt.Errorf("producer with address %s and id %d is not in the current producer set", msg.Producer, producerId)
}

// Reject non-default target before the fork height.
if msg.TargetProducerId != types.RoundRobinDefault && sdkCtx.BlockHeight() < helper.GetTargetProducerOverrideHeight() {
return nil, fmt.Errorf("target producer override is not enabled until height %d", helper.GetTargetProducerOverrideHeight())
}

// Validate target producer if it is not the round-robin default and the block height is greater than the target producer override height.
if msg.TargetProducerId != types.RoundRobinDefault && sdkCtx.BlockHeight() >= helper.GetTargetProducerOverrideHeight() {
if msg.TargetProducerId == producerId {
return nil, fmt.Errorf("target producer cannot be the same as the current producer")
}
targetInCandidates := false
for _, c := range candidates {
if c == msg.TargetProducerId {
targetInCandidates = true
break
}
}
if !targetInCandidates {
return nil, fmt.Errorf("target producer id %d is not in the current producer set", msg.TargetProducerId)
}
}

return &types.MsgSetProducerDowntimeResponse{}, nil
}

Expand Down
94 changes: 93 additions & 1 deletion x/bor/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,6 @@ func (s *KeeperTestSuite) TestBackfillSpans() {
{
name: "mismatch between calculated and provided last span id",
backfillSpans: types.MsgBackfillSpans{

Proposer: common.HexToAddress("someProposer").String(),
ChainId: testChainParams.ChainParams.BorChainId,
LatestSpanId: 1,
Expand Down Expand Up @@ -699,7 +698,87 @@ func (s *KeeperTestSuite) TestSetProducerDowntime() {
seedVotes: []uint64{id1, id2},
msg: newMsg(val1.hexAddr, 600, 600+maxRange),
},
{
name: "success - target zero accepted (round-robin default)",
validators: []staketypes.Validator{{ValId: val1.id, Signer: val1.hexAddr}, {ValId: val2.id, Signer: val2.hexAddr}},
seedVotes: []uint64{id1, id2, id3},
msg: &types.MsgSetProducerDowntime{
Producer: val1.hexAddr,
DowntimeRange: types.BlockRange{StartBlock: 100, EndBlock: 100 + minRange},
TargetProducerId: types.RoundRobinDefault,
},
},
{
name: "success - valid target in producer set",
validators: []staketypes.Validator{{ValId: val1.id, Signer: val1.hexAddr}, {ValId: val2.id, Signer: val2.hexAddr}, {ValId: val3.id, Signer: val3.hexAddr}},
seedVotes: []uint64{id1, id2, id3},
msg: &types.MsgSetProducerDowntime{
Producer: val1.hexAddr,
DowntimeRange: types.BlockRange{StartBlock: 100, EndBlock: 100 + minRange},
TargetProducerId: id2,
},
},
{
name: "error - target is the current producer",
validators: []staketypes.Validator{{ValId: val1.id, Signer: val1.hexAddr}, {ValId: val2.id, Signer: val2.hexAddr}},
seedVotes: []uint64{id1, id2, id3},
msg: &types.MsgSetProducerDowntime{
Producer: val1.hexAddr,
DowntimeRange: types.BlockRange{StartBlock: 100, EndBlock: 100 + minRange},
TargetProducerId: id1,
},
expectErrSubstr: "target producer cannot be the same as the current producer",
},
{
name: "error - target not in producer set",
validators: []staketypes.Validator{{ValId: val1.id, Signer: val1.hexAddr}, {ValId: val2.id, Signer: val2.hexAddr}, {ValId: val3.id, Signer: val3.hexAddr}},
seedVotes: []uint64{id1, id2}, // only id1 and id2 qualify; id3 does not
msg: &types.MsgSetProducerDowntime{
Producer: val1.hexAddr,
DowntimeRange: types.BlockRange{StartBlock: 100, EndBlock: 100 + minRange},
TargetProducerId: id3,
},
expectErrSubstr: "target producer id 3 is not in the current producer set",
},
{
name: "error - target is non-existent validator",
validators: []staketypes.Validator{{ValId: val1.id, Signer: val1.hexAddr}, {ValId: val2.id, Signer: val2.hexAddr}},
seedVotes: []uint64{id1, id2, id3},
msg: &types.MsgSetProducerDowntime{
Producer: val1.hexAddr,
DowntimeRange: types.BlockRange{StartBlock: 100, EndBlock: 100 + minRange},
TargetProducerId: 999,
},
expectErrSubstr: "target producer id 999 is not in the current producer set",
},
}

// Pre-fork rejection: when targetProducerOverrideHeight is set high,
// non-zero target should be rejected.
preForkTests := []testCase{
{
name: "error - target rejected before fork height",
validators: []staketypes.Validator{{ValId: val1.id, Signer: val1.hexAddr}, {ValId: val2.id, Signer: val2.hexAddr}, {ValId: val3.id, Signer: val3.hexAddr}},
seedVotes: []uint64{id1, id2, id3},
msg: &types.MsgSetProducerDowntime{
Producer: val1.hexAddr,
DowntimeRange: types.BlockRange{StartBlock: 100, EndBlock: 100 + minRange},
TargetProducerId: id2,
},
expectErrSubstr: "target producer override is not enabled until height",
},
{
name: "success - round-robin default accepted before fork height",
validators: []staketypes.Validator{{ValId: val1.id, Signer: val1.hexAddr}, {ValId: val2.id, Signer: val2.hexAddr}},
seedVotes: []uint64{id1, id2, id3},
msg: &types.MsgSetProducerDowntime{
Producer: val1.hexAddr,
DowntimeRange: types.BlockRange{StartBlock: 100, EndBlock: 100 + minRange},
TargetProducerId: types.RoundRobinDefault,
},
},
}
tests = append(tests, preForkTests...)

for _, tc := range tests {
s.T().Run(tc.name, func(t *testing.T) {
Expand All @@ -708,6 +787,19 @@ func (s *KeeperTestSuite) TestSetProducerDowntime() {
ctx := s.ctx
msgServer := s.msgServer

// Set fork height high for pre-fork tests, restore after.
isPreFork := false
for _, pf := range preForkTests {
if pf.name == tc.name {
isPreFork = true
break
}
}
if isPreFork {
helper.SetTargetProducerOverrideHeight(999999)
defer helper.SetTargetProducerOverrideHeight(0)
}

// Prime mocks and seed producer votes controlling the producer set
primeStakeMocks(tc.validators)
setVotesForAll(tc.seedVotes)
Expand Down
Loading
Loading