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
1 change: 1 addition & 0 deletions consensus/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type RoundHandler interface {
TimeDuration() time.Duration
RemainingTime(startTime time.Time, maxTime time.Duration) time.Duration
GetTimeStampForRound(round uint64) uint64
ComputeCurrentRound() int64
IsInterfaceNil() bool
}

Expand Down
37 changes: 33 additions & 4 deletions consensus/round/round.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ func NewRound(args ArgsRound) (*round, error) {

// UpdateRound updates the index and the time stamp of the round depending on the genesis time and the current time given
func (rnd *round) UpdateRound(genesisTimeStamp time.Time, currentTimeStamp time.Time) {
baseTimeStamp, roundDuration, startRound := rnd.getBaseInfo(genesisTimeStamp, currentTimeStamp)
rnd.updateRound(baseTimeStamp, currentTimeStamp, startRound, roundDuration)
}

func (rnd *round) getBaseInfo(
genesisTimeStamp time.Time,
currentTimeStamp time.Time,
) (time.Time, time.Duration, int64) {
baseTimeStamp := rnd.supernovaGenesisTimeStamp
roundDuration := rnd.supernovaTimeDuration
startRound := rnd.supernovaStartRound
Expand All @@ -95,7 +103,20 @@ func (rnd *round) UpdateRound(genesisTimeStamp time.Time, currentTimeStamp time.
startRound = rnd.startRound
}

rnd.updateRound(baseTimeStamp, currentTimeStamp, startRound, roundDuration)
return baseTimeStamp, roundDuration, startRound
}

func getIndex(
genesisTimeStamp time.Time,
currentTimeStamp time.Time,
roundDuration time.Duration,
startRound int64,
) (int64, int64) {
delta := currentTimeStamp.Sub(genesisTimeStamp).Nanoseconds()

index := int64(math.Floor(float64(delta)/float64(roundDuration.Nanoseconds()))) + startRound

return index, delta
}

func (rnd *round) isSupernovaRoundActivated() bool {
Expand Down Expand Up @@ -134,9 +155,7 @@ func (rnd *round) updateRound(
startRound int64,
roundDuration time.Duration,
) {
delta := currentTimeStamp.Sub(genesisTimeStamp).Nanoseconds()

index := int64(math.Floor(float64(delta)/float64(roundDuration.Nanoseconds()))) + startRound
index, delta := getIndex(genesisTimeStamp, currentTimeStamp, roundDuration, startRound)

rnd.Lock()
if rnd.index != index {
Expand Down Expand Up @@ -226,6 +245,16 @@ func (rnd *round) GetTimeStampForRound(round uint64) uint64 {
return uint64(roundTimeStampMs)
}

// ComputeCurrentRound computes the round that should match the current timestamp
func (rnd *round) ComputeCurrentRound() int64 {
genesisTimeStamp := rnd.genesisTimeStamp
currentTimeStamp := rnd.syncTimer.CurrentTime()
timeStamp, roundDuration, startRound := rnd.getBaseInfo(genesisTimeStamp, currentTimeStamp)
index, _ := getIndex(timeStamp, currentTimeStamp, roundDuration, startRound)

return index
}

// IsInterfaceNil returns true if there is no value under the interface
func (rnd *round) IsInterfaceNil() bool {
return rnd == nil
Expand Down
68 changes: 68 additions & 0 deletions consensus/round/round_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,74 @@ func TestRound_Concurrency(t *testing.T) {
})
}

func TestRound_ComputeCurrentRound(t *testing.T) {
t.Parallel()

t.Run("before supernova should return correct round", func(t *testing.T) {
t.Parallel()

genesisTime := time.Now()
currentTime := genesisTime.Add(3 * roundTimeDuration)

syncTimerMock := &consensusMocks.SyncTimerMock{
CurrentTimeCalled: func() time.Time {
return currentTime
},
}

args := createDefaultRoundArgs()
args.GenesisTimeStamp = genesisTime
args.SupernovaGenesisTimeStamp = genesisTime.Add(10 * roundTimeDuration)
args.SyncTimer = syncTimerMock

rnd, err := round.NewRound(args)
require.Nil(t, err)

computedRound := rnd.ComputeCurrentRound()
assert.Equal(t, int64(3), computedRound)
})

t.Run("after supernova should return correct round", func(t *testing.T) {
t.Parallel()

genesisTime := time.Now()
roundDuration := 10 * time.Millisecond
supernovaRoundDuration := 5 * time.Millisecond
supernovaStartRound := int64(5)
supernovaGenesisTime := genesisTime.Add(time.Duration(supernovaStartRound) * roundDuration)

// current time is 3 supernova rounds after supernova genesis
currentTime := supernovaGenesisTime.Add(3 * supernovaRoundDuration)

syncTimerMock := &consensusMocks.SyncTimerMock{
CurrentTimeCalled: func() time.Time {
return currentTime
},
}

args := createDefaultRoundArgs()
args.GenesisTimeStamp = genesisTime
args.SupernovaGenesisTimeStamp = supernovaGenesisTime
args.RoundTimeDuration = roundDuration
args.SupernovaTimeDuration = supernovaRoundDuration
args.SupernovaStartRound = supernovaStartRound
args.CurrentTimeStamp = currentTime
args.SyncTimer = syncTimerMock
args.EnableRoundsHandler = &testscommon.EnableRoundsHandlerStub{
IsFlagEnabledInRoundCalled: func(flag common.EnableRoundFlag, round uint64) bool {
return flag == common.SupernovaRoundFlag && round >= uint64(supernovaStartRound)
},
}

rnd, err := round.NewRound(args)
require.Nil(t, err)

// delta from supernovaGenesis = 3*5ms = 15ms, index = floor(15ms/5ms) + 5 = 3 + 5 = 8
computedRound := rnd.ComputeCurrentRound()
assert.Equal(t, int64(8), computedRound)
})
}

func TestRound_GetTimeStampForRound(t *testing.T) {
t.Parallel()

Expand Down
4 changes: 2 additions & 2 deletions consensus/spos/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const sleepTime = 5 * time.Millisecond
const redundancySingleKeySteppedIn = "single-key node stepped in"

type blockProcessorWithPool interface {
RemoveHeaderFromPool(headerHash []byte)
RemoveHeaderFromPool(headerNonce uint64)
}

// Worker defines the data needed by spos to communicate between nodes which are in the validators group
Expand Down Expand Up @@ -913,7 +913,7 @@ func (wrk *Worker) removeConsensusHeaderFromPool() {
return
}

blockProcessorWithPoolAccess.RemoveHeaderFromPool(headerHash)
blockProcessorWithPoolAccess.RemoveHeaderFromPool(header.GetNonce())
wrk.forkDetector.RemoveHeader(header.GetNonce(), headerHash)
}

Expand Down
30 changes: 25 additions & 5 deletions epochStart/mock/rounderStub.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import (
type RoundHandlerStub struct {
RoundIndex int64

IndexCalled func() int64
TimeDurationCalled func() time.Duration
TimeStampCalled func() time.Time
UpdateRoundCalled func(time.Time, time.Time)
RemainingTimeCalled func(startTime time.Time, maxTime time.Duration) time.Duration
IndexCalled func() int64
TimeDurationCalled func() time.Duration
TimeStampCalled func() time.Time
UpdateRoundCalled func(time.Time, time.Time)
RemainingTimeCalled func(startTime time.Time, maxTime time.Duration) time.Duration
GetTimeStampForRoundCalled func(round uint64) uint64
ComputeCurrentRoundCalled func() int64
}

// Index -
Expand Down Expand Up @@ -61,6 +63,24 @@ func (rndm *RoundHandlerStub) RemainingTime(startTime time.Time, maxTime time.Du
return 4000 * time.Millisecond
}

// GetTimeStampForRound -
func (rndm *RoundHandlerStub) GetTimeStampForRound(round uint64) uint64 {
if rndm.GetTimeStampForRoundCalled != nil {
return rndm.GetTimeStampForRoundCalled(round)
}

return uint64(time.Unix(0, 0).UnixMilli())
}

// ComputeCurrentRound -
func (rndm *RoundHandlerStub) ComputeCurrentRound() int64 {
if rndm.ComputeCurrentRoundCalled != nil {
return rndm.ComputeCurrentRoundCalled()
}

return rndm.RoundIndex
}

// IsInterfaceNil returns true if there is no value under the interface
func (rndm *RoundHandlerStub) IsInterfaceNil() bool {
return rndm == nil
Expand Down
20 changes: 15 additions & 5 deletions integrationTests/mock/roundHandlerMock.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import "time"

// RoundHandlerMock -
type RoundHandlerMock struct {
IndexField int64
TimeStampField time.Time
TimeDurationField time.Duration
RemainingTimeField time.Duration
BeforeGenesisCalled func() bool
IndexField int64
TimeStampField time.Time
TimeDurationField time.Duration
RemainingTimeField time.Duration
BeforeGenesisCalled func() bool
ComputeCurrentRoundCalled func() int64
}

// BeforeGenesis -
Expand Down Expand Up @@ -58,6 +59,15 @@ func (mock *RoundHandlerMock) GetTimeStampForRound(round uint64) uint64 {
return round * uint64(mock.TimeDuration().Milliseconds())
}

// ComputeCurrentRound -
func (mock *RoundHandlerMock) ComputeCurrentRound() int64 {
if mock.ComputeCurrentRoundCalled != nil {
return mock.ComputeCurrentRoundCalled()
}

return mock.IndexField
}

// IsInterfaceNil -
func (mock *RoundHandlerMock) IsInterfaceNil() bool {
return mock == nil
Expand Down
5 changes: 4 additions & 1 deletion integrationTests/testProcessorNode.go
Original file line number Diff line number Diff line change
Expand Up @@ -3547,7 +3547,10 @@ func (tpn *TestProcessorNode) MiniBlocksPresent(hashes [][]byte) bool {
}

func (tpn *TestProcessorNode) initRoundHandler(roundTime time.Duration) {
tpn.RoundHandler = &mock.RoundHandlerMock{TimeDurationField: roundTime}
tpn.RoundHandler = &mock.RoundHandlerMock{
TimeDurationField: roundTime,
RemainingTimeField: roundTime,
}
}

func (tpn *TestProcessorNode) initRequestedItemsHandler() {
Expand Down
5 changes: 5 additions & 0 deletions node/chainSimulator/components/manualRoundHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ func (handler *manualRoundHandler) GetTimeStampForRound(round uint64) uint64 {

}

// ComputeCurrentRound returns the current index
func (handler *manualRoundHandler) ComputeCurrentRound() int64 {
return handler.index
}

// IsInterfaceNil returns true if there is no value under the interface
func (handler *manualRoundHandler) IsInterfaceNil() bool {
return handler == nil
Expand Down
4 changes: 2 additions & 2 deletions process/block/baseProcess.go
Original file line number Diff line number Diff line change
Expand Up @@ -2415,9 +2415,9 @@ func (bp *baseProcessor) restoreBlockBody(headerHandler data.HeaderHandler, body
}

// RemoveHeaderFromPool removes the header from the pool
func (bp *baseProcessor) RemoveHeaderFromPool(headerHash []byte) {
func (bp *baseProcessor) RemoveHeaderFromPool(headerNonce uint64) {
headersPool := bp.dataPool.Headers()
headersPool.RemoveHeaderByHash(headerHash)
headersPool.RemoveHeaderByNonceAndShardId(headerNonce, bp.shardCoordinator.SelfId())
}

// RestoreBlockBodyIntoPools restores the block body into associated pools
Expand Down
2 changes: 2 additions & 0 deletions process/block/interceptedBlocks/argInterceptedBlockHeader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package interceptedBlocks
import (
"github.com/multiversx/mx-chain-core-go/hashing"
"github.com/multiversx/mx-chain-core-go/marshal"
"github.com/multiversx/mx-chain-go/p2p"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/process"
Expand All @@ -21,4 +22,5 @@ type ArgInterceptedBlockHeader struct {
EpochStartTrigger process.EpochStartTriggerHandler
EnableEpochsHandler common.EnableEpochsHandler
EpochChangeGracePeriodHandler common.EpochChangeGracePeriodHandler
BroadcastMethod p2p.BroadcastMethod
}
11 changes: 8 additions & 3 deletions process/block/interceptedBlocks/interceptedBlockHeader.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/multiversx/mx-chain-core-go/data"
"github.com/multiversx/mx-chain-core-go/data/block"
"github.com/multiversx/mx-chain-core-go/hashing"
"github.com/multiversx/mx-chain-go/p2p"
logger "github.com/multiversx/mx-chain-logger-go"

"github.com/multiversx/mx-chain-go/common"
Expand All @@ -31,6 +32,7 @@ type InterceptedHeader struct {
epochStartTrigger process.EpochStartTriggerHandler
enableEpochsHandler common.EnableEpochsHandler
epochChangeGracePeriodHandler common.EpochChangeGracePeriodHandler
broadcastMethod p2p.BroadcastMethod
}

// NewInterceptedHeader creates a new instance of InterceptedHeader struct
Expand All @@ -55,6 +57,7 @@ func NewInterceptedHeader(arg *ArgInterceptedBlockHeader) (*InterceptedHeader, e
epochStartTrigger: arg.EpochStartTrigger,
enableEpochsHandler: arg.EnableEpochsHandler,
epochChangeGracePeriodHandler: arg.EpochChangeGracePeriodHandler,
broadcastMethod: arg.BroadcastMethod,
}
inHdr.processFields(arg.HdrBuff)

Expand Down Expand Up @@ -154,9 +157,11 @@ func (inHdr *InterceptedHeader) integrity() error {
}
}

err = inHdr.validityAttester.CheckBlockAgainstRoundHandler(inHdr.HeaderHandler())
if err != nil {
return err
if inHdr.broadcastMethod == p2p.Broadcast {
err = inHdr.validityAttester.CheckBlockAgainstRoundHandler(inHdr.HeaderHandler())
if err != nil {
return err
}
}

err = checkMiniBlocksHeaders(inHdr.hdr.GetMiniBlockHeaderHandlers(), inHdr.shardCoordinator)
Expand Down
11 changes: 11 additions & 0 deletions process/block/interceptedBlocks/interceptedBlockHeader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/multiversx/mx-chain-core-go/data"
dataBlock "github.com/multiversx/mx-chain-core-go/data/block"
"github.com/multiversx/mx-chain-core-go/marshal"
"github.com/multiversx/mx-chain-go/p2p"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -45,6 +46,7 @@ func createDefaultShardArgument() *interceptedBlocks.ArgInterceptedBlockHeader {
EpochStartTrigger: &mock.EpochStartTriggerStub{},
EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{},
EpochChangeGracePeriodHandler: gracePeriod,
BroadcastMethod: p2p.Broadcast,
}

hdr := createMockShardHeader()
Expand All @@ -65,6 +67,7 @@ func createDefaultShardArgumentWithV2Support() *interceptedBlocks.ArgIntercepted
EpochStartTrigger: &mock.EpochStartTriggerStub{},
EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{},
EpochChangeGracePeriodHandler: gracePeriod,
BroadcastMethod: p2p.Broadcast,
}
hdr := createMockShardHeader()
arg.HdrBuff, _ = arg.Marshalizer.Marshal(hdr)
Expand Down Expand Up @@ -113,6 +116,7 @@ func createDefaultShardArgumentWithV3Support() *interceptedBlocks.ArgIntercepted
},
},
EpochChangeGracePeriodHandler: gracePeriod,
BroadcastMethod: p2p.Broadcast,
}
hdr := createMockShardHeaderV3()
arg.HdrBuff, _ = arg.Marshalizer.Marshal(hdr)
Expand Down Expand Up @@ -639,6 +643,13 @@ func TestInterceptedHeader_CheckValidityShouldWorkHeaderV3(t *testing.T) {
t.Parallel()

arg := createDefaultShardArgumentWithV3Support()
arg.BroadcastMethod = p2p.Direct
arg.ValidityAttester = &mock.ValidityAttesterStub{
CheckBlockAgainstRoundHandlerCalled: func(headerHandler data.HeaderHandler) error {
require.Fail(t, "should not be called")
return nil
},
}
inHdr, err := interceptedBlocks.NewInterceptedHeader(arg)
assert.Nil(t, err)
assert.NotNil(t, inHdr)
Expand Down
Loading
Loading