Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
40 changes: 35 additions & 5 deletions bridge/setu/processor/checkpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,16 +848,36 @@ func (cp *CheckpointProcessor) checkIfNoAckIsRequired(checkpointContext *Checkpo
currentTime := time.Now().UTC()

timeDiff := currentTime.Sub(checkpointCreationTime)
if timeDiff.Seconds() >= helper.GetConfig().CheckpointerPollInterval.Seconds() && index == 0 {
index = math.Floor(timeDiff.Seconds() / helper.GetConfig().CheckpointerPollInterval.Seconds())

// checkpoint params
checkpointParams := checkpointContext.CheckpointParams

var checkpointPollInterval time.Duration
if checkpointParams.CheckpointPollInterval > 0 {
checkpointPollInterval = checkpointParams.CheckpointPollInterval
} else {
checkpointPollInterval = helper.GetConfig().CheckpointerPollInterval
}

if index == 0 {
var checkpointTimeout time.Duration
isOpen, tronMaxLength, err := cp.getTronDynamicCheckpointProposalWithErr()
if err != nil {
cp.Logger.Error("failed to check if no ack is required. Error while fetching dynamic checkpoint feature", "error", err)
return false, uint64(index)
}
if isOpen {
checkpointTimeout, _ = helper.CalcCheckpointTimeout(tronMaxLength, checkpointPollInterval)
} else {
checkpointTimeout = checkpointPollInterval
}

// checkpoint params
checkpointParams := checkpointContext.CheckpointParams
if timeDiff.Seconds() >= checkpointTimeout.Seconds() && index == 0 {
index = math.Floor(timeDiff.Seconds() / checkpointTimeout.Seconds())
}

if index == 0 {
return false, uint64(index)
}

// check if difference between no-ack time and current time
lastNoAck := cp.getLastNoAckTime()
Expand Down Expand Up @@ -1096,3 +1116,13 @@ func (cp *CheckpointProcessor) getDynamicCheckpointProposal(rootType string) (bo

return fea.IsOpen, fea.IntConf[strings.ToLower(rootType)] != 0, fea.IntConf["maxLength"]
}

func (cp *CheckpointProcessor) getTronDynamicCheckpointProposalWithErr() (bool, int, error) {
fea, err := util.GetTronDynamicCheckpointFeature(cp.cliCtx)
if err != nil {
cp.Logger.Error("Error while fetching dynamic checkpoint feature", "error", err)

return false, 0, err
}
return fea.IsOpen, fea.IntConf["maxLength"], err
}
197 changes: 197 additions & 0 deletions bridge/setu/processor/checkpoint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package processor

import (
"context"
cliContext "github.com/cosmos/cosmos-sdk/client/context"
"github.com/maticnetwork/heimdall/bridge/setu/util"
"github.com/stretchr/testify/mock"
"github.com/tendermint/tendermint/libs/log"
"testing"
"time"
)

// MockLogger is a mock implementation of the Logger interface
type MockLogger struct {
mock.Mock
}

func (m *MockLogger) Info(msg string, keysAndValues ...interface{}) {
m.Called(msg, keysAndValues)
}

func (m *MockLogger) Debug(msg string, keysAndValues ...interface{}) {
m.Called(msg, keysAndValues)
}

func (m *MockLogger) Error(msg string, keysAndValues ...interface{}) {
m.Called(msg, keysAndValues)
}

func TestStartPolling(t *testing.T) {
logger := util.Logger().With("service", "processor", "module", "checkpoint")
if logger == nil {
logger = log.NewNopLogger()
}
// Setup mock CheckpointProcessor
cp := &CheckpointProcessor{
BaseProcessor: BaseProcessor{
Logger: logger,
cliCtx: cliContext.CLIContext{},
},
}
ackCtx, cancelNoACKPolling := context.WithCancel(context.Background())
cp.cancelNoACKPolling = cancelNoACKPolling
go cp.startPolling(ackCtx)
time.Sleep(10 * time.Second)
cancelNoACKPolling()
logger.Info("Polling cancelled and test Stopped")
//need to see the log "Polling cancelled while waiting for checkpoint params"

}

// function GetTronDynamicCheckpointFeature should change to the following format temporarily : var GetTronDynamicCheckpointFeature = func(cliCtx cliContext.CLIContext) (*featureManagerTypes.PlainFeatureData, error)
//func TestGetTronDynamicCheckpointProposal(t *testing.T) {
// // Setup mock logger
// mockLogger := new(MockLogger)
// mockLogger.On("Error", mock.Anything, mock.Anything, mock.Anything).Return()
//
// logger := util.Logger().With("service", "processor", "module", "checkpoint")
// if logger == nil {
// logger = log.NewNopLogger()
// }
// // Setup mock CheckpointProcessor
// cp := &CheckpointProcessor{
// BaseProcessor: BaseProcessor{
// Logger: logger,
// cliCtx: cliContext.CLIContext{},
// },
// }
//
// // Test case 1: Successful feature fetch
// t.Run("Successful feature fetch", func(t *testing.T) {
// // Mock util.GetTronDynamicCheckpointFeature
// originalGetTronDynamicCheckpointFeature := util.GetTronDynamicCheckpointFeature
// defer func() { util.GetTronDynamicCheckpointFeature = originalGetTronDynamicCheckpointFeature }()
// util.GetTronDynamicCheckpointFeature = func(cliCtx cliContext.CLIContext) (*featureManagerTypes.PlainFeatureData, error) {
// return &featureManagerTypes.PlainFeatureData{
// IsOpen: true,
// IntConf: map[string]int{
// "maxLength": 1024,
// },
// }, nil
// }
//
// isOpen, maxLength := cp.getTronDynamicCheckpointProposal()
// assert.True(t, isOpen)
// assert.Equal(t, 1024, maxLength)
// })
//
// // Test case 2: Error fetching feature
// t.Run("Error fetching feature", func(t *testing.T) {
// // Mock util.GetTronDynamicCheckpointFeature
// originalGetTronDynamicCheckpointFeature := util.GetTronDynamicCheckpointFeature
// defer func() { util.GetTronDynamicCheckpointFeature = originalGetTronDynamicCheckpointFeature }()
// util.GetTronDynamicCheckpointFeature = func(cliCtx cliContext.CLIContext) (*featureManagerTypes.PlainFeatureData, error) {
// return nil, errors.New("some error")
// }
//
// isOpen, maxLength := cp.getTronDynamicCheckpointProposal()
// assert.False(t, isOpen)
// assert.Equal(t, 0, maxLength)
// //mockLogger.AssertCalled(t, "Error", "Error while fetching dynamic checkpoint feature", "error", errors.New("some error"))
// })
//}

//// function GetCheckpointParams should change to the following format temporarily : var GetCheckpointParams = func(cliCtx cliContext.CLIContext) (*checkpointTypes.Params, error)
//// so do the function GetTronDynamicCheckpointFeature
//func TestGetCheckpointPollTimeWithTronDynamic(t *testing.T) {
// // Setup mock logger
// mockLogger := new(MockLogger)
// mockLogger.On("Error", mock.Anything, mock.Anything, mock.Anything).Return()
//
// logger := util.Logger().With("service", "processor", "module", "checkpoint")
// if logger == nil {
// logger = log.NewNopLogger()
// }
// // Setup mock CheckpointProcessor
// cp := &CheckpointProcessor{
// BaseProcessor: BaseProcessor{
// Logger: logger,
// cliCtx: cliContext.CLIContext{},
// },
// }
//
// // Test case 1: Successful feature fetch
// t.Run("Successful feature fetch", func(t *testing.T) {
// // Mock util.GetTronDynamicCheckpointFeature
// originalGetTronDynamicCheckpointFeature := util.GetTronDynamicCheckpointFeature
// defer func() { util.GetTronDynamicCheckpointFeature = originalGetTronDynamicCheckpointFeature }()
// util.GetTronDynamicCheckpointFeature = func(cliCtx cliContext.CLIContext) (*featureManagerTypes.PlainFeatureData, error) {
// return &featureManagerTypes.PlainFeatureData{
// IsOpen: true,
// IntConf: map[string]int{
// "maxLength": 1024,
// },
// }, nil
// }
//
// originalGetCheckpointParams := util.GetCheckpointParams
// defer func() { util.GetCheckpointParams = originalGetCheckpointParams }()
// util.GetCheckpointParams = func(cliCtx cliContext.CLIContext) (*checkpointTypes.Params, error) {
// return &checkpointTypes.Params{
// CheckpointBufferTime: 1000 * time.Second,
// CheckpointPollInterval: 10 * time.Minute,
// }, nil
// }
//
// pollTime, err := cp.GetCheckpointPollTime()
// assert.Equal(t, 10*time.Minute, pollTime)
// assert.Nil(t, err)
// })
//}
//
//// function GetCheckpointParams should change to the following format temporarily : var GetCheckpointParams = func(cliCtx cliContext.CLIContext) (*checkpointTypes.Params, error)
//// so do the function GetTronDynamicCheckpointFeature
//func TestGetCheckpointPollTimeWithoutTronDynamic(t *testing.T) {
// helper.SetTestConfig(helper.GetDefaultHeimdallConfig())
// // Setup mock logger
// mockLogger := new(MockLogger)
// mockLogger.On("Error", mock.Anything, mock.Anything, mock.Anything).Return()
//
// logger := util.Logger().With("service", "processor", "module", "checkpoint")
// if logger == nil {
// logger = log.NewNopLogger()
// }
// // Setup mock CheckpointProcessor
// cp := &CheckpointProcessor{
// BaseProcessor: BaseProcessor{
// Logger: logger,
// cliCtx: cliContext.CLIContext{},
// },
// }
//
// // Test case 1: Successful feature fetch
// t.Run("Successful feature fetch", func(t *testing.T) {
// // Mock util.GetTronDynamicCheckpointFeature
// originalGetTronDynamicCheckpointFeature := util.GetTronDynamicCheckpointFeature
// defer func() { util.GetTronDynamicCheckpointFeature = originalGetTronDynamicCheckpointFeature }()
// util.GetTronDynamicCheckpointFeature = func(cliCtx cliContext.CLIContext) (*featureManagerTypes.PlainFeatureData, error) {
// return &featureManagerTypes.PlainFeatureData{
// IsOpen: false,
// }, nil
// }
//
// originalGetCheckpointParams := util.GetCheckpointParams
// defer func() { util.GetCheckpointParams = originalGetCheckpointParams }()
// util.GetCheckpointParams = func(cliCtx cliContext.CLIContext) (*checkpointTypes.Params, error) {
// return &checkpointTypes.Params{
// CheckpointBufferTime: 1000 * time.Second,
// CheckpointPollInterval: 10 * time.Minute,
// }, nil
// }
//
// pollTime, err := cp.GetCheckpointPollTime()
// assert.Equal(t, 30*time.Minute, pollTime)
// assert.Nil(t, err)
// })
//}
4 changes: 4 additions & 0 deletions bridge/setu/util/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,10 @@ func GetDynamicCheckpointFeature(cliCtx cliContext.CLIContext) (*featureManagerT
return GetTargetFeatureConfig(cliCtx, featureManagerTypes.DynamicCheckpoint)
}

func GetTronDynamicCheckpointFeature(cliCtx cliContext.CLIContext) (*featureManagerTypes.PlainFeatureData, error) {
return GetTargetFeatureConfig(cliCtx, featureManagerTypes.TronDynamicCheckpoint)
}

func GetFinalizedEthOpen(cliCtx cliContext.CLIContext) bool {
feature, err := GetTargetFeatureConfig(cliCtx, featureManagerTypes.FinalizedEth)
if err != nil {
Expand Down
18 changes: 17 additions & 1 deletion checkpoint/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,29 @@ func handleMsgCheckpointNoAck(ctx sdk.Context, msg types.MsgCheckpointNoAck, k K
// Get buffer time from params
bufferTime := k.GetParams(ctx).CheckpointBufferTime

var checkpointPollInterval time.Duration
if k.GetParams(ctx).CheckpointPollInterval > 0 {
checkpointPollInterval = k.GetParams(ctx).CheckpointPollInterval
} else {
checkpointPollInterval = helper.GetConfig().CheckpointerPollInterval
}

var checkpointTimeout time.Duration
tronDynamicFeature := util.GetFeatureConfig().GetFeature(ctx, featuremanagerTypes.TronDynamicCheckpoint)
if tronDynamicFeature.IsOpen {
tronMaxLength := tronDynamicFeature.IntConf["maxLength"]
checkpointTimeout, _ = helper.CalcCheckpointTimeout(tronMaxLength, checkpointPollInterval)
} else {
checkpointTimeout = checkpointPollInterval
}

// Fetch last checkpoint from store
// TODO figure out how to handle this error
lastCheckpoint, _ := k.GetLastCheckpoint(ctx, hmTypes.RootChainTypeStake)
lastCheckpointTime := time.Unix(int64(lastCheckpoint.TimeStamp), 0)

// If last checkpoint is not present or last checkpoint happens before checkpoint buffer time -- thrown an error
if lastCheckpointTime.After(currentTime) || (currentTime.Sub(lastCheckpointTime) < bufferTime) {
if lastCheckpointTime.After(currentTime) || (currentTime.Sub(lastCheckpointTime) < checkpointTimeout) {
logger.Debug("Invalid No ACK -- Waiting for last checkpoint ACK")
return common.ErrInvalidNoACK(k.Codespace()).Result()
}
Expand Down
68 changes: 68 additions & 0 deletions checkpoint/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/maticnetwork/heimdall/checkpoint"
chSim "github.com/maticnetwork/heimdall/checkpoint/simulation"

featuremanagerTypes "github.com/maticnetwork/heimdall/featuremanager/types"
"github.com/maticnetwork/heimdall/helper/mocks"
hmTypes "github.com/maticnetwork/heimdall/types"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -367,6 +368,73 @@ func (suite *HandlerTestSuite) TestHandleMsgCheckpointNoAck() {
require.Equal(t, uint64(0), uint64(ackCount), "Should not update state")
}

func (suite *HandlerTestSuite) TestHandleMsgCheckpointNoAckWithTronDynamicOpen() {
t, app, ctx := suite.T(), suite.app, suite.ctx
keeper := app.CheckpointKeeper
stakingKeeper := app.StakingKeeper
topupKeeper := app.TopupKeeper
featureKeeper := app.FeatureKeeper
start := uint64(0)
maxSize := uint64(256)
params := keeper.GetParams(ctx)

open := true

featureParams := featuremanagerTypes.FeatureParams{
FeatureParamMap: map[string]featuremanagerTypes.FeatureData{
featuremanagerTypes.DynamicCheckpoint: {IsOpen: &open,
IntConf: map[string]int{
"eth": 1,
"maxLength": 1024,
}},
featuremanagerTypes.FinalizedEth: {IsOpen: &open},
},
}
featureData := featuremanagerTypes.FeatureData{
IsOpen: &open,
IntConf: map[string]int{
"maxLength": 1024,
},
}
// 添加数据
featureParams.FeatureParamMap[featuremanagerTypes.TronDynamicCheckpoint] = featureData

featureKeeper.SetFeatureParams(ctx, featureParams)

dividendAccount := hmTypes.DividendAccount{
User: hmTypes.HexToHeimdallAddress("123"),
FeeAmount: big.NewInt(0).String(),
}
topupKeeper.AddDividendAccount(ctx, dividendAccount)

// check valid checkpoint
// generate proposer for validator set
chSim.LoadValidatorSet(2, t, stakingKeeper, ctx, false, 10)
stakingKeeper.IncrementAccum(ctx, 1)

lastCheckpoint, err := keeper.GetLastCheckpoint(ctx, hmTypes.RootChainTypeStake)
if err == nil {
start = start + lastCheckpoint.EndBlock + 1
}

header, err := chSim.GenRandCheckpoint(start, maxSize, params.MaxCheckpointLength)

// add current proposer to header
header.Proposer = stakingKeeper.GetValidatorSet(ctx).Proposer.Signer

got := suite.SendCheckpoint(header)
require.True(t, got.IsOK(), "expected send-NoAck to be ok, got %v", got)

// set time lastCheckpoint timestamp + checkpointBufferTime
checkpointTimeout := 40 * time.Minute
newTime := lastCheckpoint.TimeStamp + uint64(checkpointTimeout)
suite.ctx = ctx.WithBlockTime(time.Unix(0, int64(newTime)))
result := suite.SendNoAck()
require.True(t, result.IsOK(), "expected send-NoAck to be ok, got %v", got)
ackCount := keeper.GetACKCount(ctx, hmTypes.RootChainTypeStake)
require.Equal(t, uint64(0), uint64(ackCount), "Should not update state")
}

func (suite *HandlerTestSuite) TestHandleMsgCheckpointNoAckBeforeBufferTimeout() {
t, app, ctx := suite.T(), suite.app, suite.ctx
keeper := app.CheckpointKeeper
Expand Down
3 changes: 1 addition & 2 deletions checkpoint/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ func createTestApp(isCheckTx bool) (*app.HeimdallApp, sdk.Context, context.CLICo
cliCtx := context.NewCLIContext().WithCodec(app.Codec())

helper.SetTestConfig(helper.GetDefaultHeimdallConfig())

params := types.NewParams(5*time.Second, 256, 1024, 10000, 30*time.Minute)
params := types.NewParams(5*time.Second, 256, 1024, 10000, 10*time.Minute)

Checkpoints := make([]hmTypes.Checkpoint, 0)

Expand Down
Loading
Loading