code.vegaprotocol.io/vega@v0.79.0/core/epochtime/service_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package epochtime_test 17 18 import ( 19 "context" 20 "testing" 21 "time" 22 23 mbroker "code.vegaprotocol.io/vega/core/broker/mocks" 24 "code.vegaprotocol.io/vega/core/epochtime" 25 "code.vegaprotocol.io/vega/core/types" 26 "code.vegaprotocol.io/vega/logging" 27 28 "github.com/golang/mock/gomock" 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 ) 32 33 var ( 34 epochs []types.Epoch 35 restored []types.Epoch 36 ) 37 38 type tstSvc struct { 39 *epochtime.Svc 40 ctrl *gomock.Controller 41 broker *mbroker.MockBroker 42 cb func(context.Context, time.Time) 43 } 44 45 func getEpochServiceMT(t *testing.T) *tstSvc { 46 t.Helper() 47 log := logging.NewTestLogger() 48 ctrl := gomock.NewController(t) 49 broker := mbroker.NewMockBroker(ctrl) 50 ret := &tstSvc{ 51 ctrl: ctrl, 52 broker: broker, 53 } 54 55 ret.Svc = epochtime.NewService( 56 log, 57 epochtime.NewDefaultConfig(), 58 broker, 59 ) 60 61 ret.cb = ret.Svc.OnTick 62 63 _ = ret.OnEpochLengthUpdate(context.Background(), time.Hour*24) // set default epoch duration 64 return ret 65 } 66 67 func onEpoch(ctx context.Context, e types.Epoch) { 68 epochs = append(epochs, e) 69 } 70 71 func onEpochRestore(ctx context.Context, e types.Epoch) { 72 restored = append(epochs, e) 73 } 74 75 func TestEpochService(t *testing.T) { 76 now := time.Unix(0, 0).UTC() 77 78 ctx := context.Background() 79 service := getEpochServiceMT(t) 80 defer service.ctrl.Finish() 81 82 service.broker.EXPECT().Send(gomock.Any()).Times(3) 83 84 // Subscribe to epoch updates 85 // Reset global used in callback so that is doesn't pick up state from another test 86 epochs = []types.Epoch{} 87 service.NotifyOnEpoch(onEpoch, onEpochRestore) 88 89 // Move time forward to generate first epoch 90 service.cb(ctx, now) 91 // Check we only have one epoch update 92 assert.Equal(t, 1, len(epochs)) 93 epoch := epochs[0] 94 // First epoch will have a 0 identifier 95 assert.EqualValues(t, 0, epoch.Seq) 96 // Start time should be the same as now 97 assert.Equal(t, now.String(), epoch.StartTime.String()) 98 // Expiry time should 1 day later 99 nextDay := now.Add(time.Hour * 24) 100 assert.Equal(t, nextDay.String(), epoch.ExpireTime.String()) 101 // End time should not be set 102 assert.True(t, epoch.EndTime.IsZero()) 103 104 // Move time forward one day + one second to start the first block past the expiry of the first epoch 105 now = now.Add((time.Hour * 24) + time.Second) 106 service.cb(ctx, now) 107 108 // end the block to mark the end of the epoch 109 service.OnBlockEnd(ctx) 110 111 // start the next block to start the second epoch 112 service.cb(ctx, now) 113 114 // We should have 2 new updates, one for end of epoch and one for the beginning of the new one 115 assert.Equal(t, 3, len(epochs)) 116 epoch = epochs[1] 117 assert.EqualValues(t, 0, epoch.Seq) 118 assert.Equal(t, now.String(), epoch.EndTime.String()) 119 120 epoch = epochs[2] 121 assert.EqualValues(t, 1, epoch.Seq) 122 assert.Equal(t, now.String(), epoch.StartTime.String()) 123 nextDay = now.Add(time.Hour * 24) 124 assert.Equal(t, nextDay.String(), epoch.ExpireTime.String()) 125 assert.True(t, epoch.EndTime.IsZero()) 126 } 127 128 // TestEpochServiceCheckpointLoading tests that when an epoch is loaded from checkpoint within the same epoch, the epoch is not prematurely ending right after the load. 129 func TestEpochServiceCheckpointLoading(t *testing.T) { 130 now := time.Unix(0, 0).UTC() 131 132 ctx := context.Background() 133 service := getEpochServiceMT(t) 134 defer service.ctrl.Finish() 135 136 service.broker.EXPECT().Send(gomock.Any()).AnyTimes() 137 138 // Move time forward to generate first epoch 139 service.cb(ctx, now) 140 141 // move to 13 hours into the epoch 142 now = now.Add(time.Hour * 13) 143 service.cb(ctx, now) 144 145 // take a checkpoint - 11h to go for the epoch 146 cp, _ := service.Checkpoint() 147 148 loadService := getEpochServiceMT(t) 149 defer loadService.ctrl.Finish() 150 loadService.broker.EXPECT().Send(gomock.Any()).AnyTimes() 151 152 loadEpochs := []types.Epoch{} 153 onLoadEpoch := func(ctx context.Context, e types.Epoch) { 154 loadEpochs = append(loadEpochs, e) 155 } 156 loadService.NotifyOnEpoch(onLoadEpoch, onEpochRestore) 157 158 // we're loading the checkpoint 4 hours after the time it was taken but we're still within the same epoch for another few good hours 159 now = now.Add((time.Hour * 4)) 160 loadService.cb(ctx, now) 161 loadService.Load(ctx, cp) 162 loadService.cb(ctx, now.Add(time.Second)) 163 // after the load we expect an event regardless of what epoch we were in before 164 require.Equal(t, 2, len(loadEpochs)) 165 166 // run to the expected end of the epoch and verify it's ended 167 now = now.Add((time.Hour * 7) + 1*time.Second) 168 loadService.cb(ctx, now) 169 require.Equal(t, 2, len(loadEpochs)) 170 171 loadService.OnBlockEnd(ctx) 172 // add another second to start a new epoch 173 now = now.Add(1 * time.Second) 174 loadService.cb(ctx, now) 175 require.Equal(t, 4, len(loadEpochs)) 176 require.Equal(t, now.String(), loadEpochs[2].EndTime.String()) 177 require.Equal(t, now.String(), loadEpochs[3].StartTime.String()) 178 } 179 180 // TestEpochServiceCheckpointFastForward tests that when an epoch is loaded from checkpoint after the epoch should have ended we're fast forwarding through the epochs that were missed. 181 func TestEpochServiceCheckpointFastForward(t *testing.T) { 182 now := time.Unix(0, 0).UTC() 183 184 ctx := context.Background() 185 service := getEpochServiceMT(t) 186 defer service.ctrl.Finish() 187 188 service.broker.EXPECT().Send(gomock.Any()).AnyTimes() 189 190 // Move time forward to generate first epoch 191 service.cb(ctx, now) 192 193 // move to 13 hours into the epoch 194 now = now.Add(time.Hour * 13) 195 service.cb(ctx, now) 196 197 // take a checkpoint - 11h to go for the epoch 198 // this epoch started at midnight and ends at next midnight 199 cp, _ := service.Checkpoint() 200 201 loadService := getEpochServiceMT(t) 202 defer loadService.ctrl.Finish() 203 loadService.broker.EXPECT().Send(gomock.Any()).AnyTimes() 204 205 loadEpochs := []types.Epoch{} 206 onLoadEpoch := func(ctx context.Context, e types.Epoch) { 207 loadEpochs = append(loadEpochs, e) 208 } 209 loadService.NotifyOnEpoch(onLoadEpoch, onEpochRestore) 210 211 // we're loading the checkpoint 4 hours after the time it was taken - so we expect the first epoch (1) to have been finished, as well as epoch 2 and 3 and epoch 4 started 212 // 72h means: 213 // finished epoch 0 at 2/1 midnight + 1 seconds 214 // started epoch 1 at 2/1 midnight + 1 seconds 215 // finished epoch 1 at 3/1 midnight + 2 seconds 216 // started epoch 2 at 3/1 midnight + 2 seconds 217 // ended epoch 2 at 4/1 midnight + 3 seconds 218 // started epoch 3 at 4/1 midnight + 3 seconds 219 // we're at 13h in epoch 3 220 now = now.Add(time.Hour * 72) 221 loadService.cb(ctx, now) 222 loadService.Load(ctx, cp) 223 224 // new block should trigger fast forward 225 loadService.cb(ctx, now.Add(time.Second)) 226 require.Equal(t, 8, len(loadEpochs)) 227 228 // to advance to the first block after the expiry we need advance by for 11h and 4 seconds 229 now = now.Add((time.Hour * 11) + 4*time.Second) 230 loadService.cb(ctx, now) 231 loadService.OnBlockEnd(ctx) 232 233 // add another second to start a new epoch 234 now = now.Add(1 * time.Second) 235 loadService.cb(ctx, now) 236 require.Equal(t, 10, len(loadEpochs)) 237 238 require.Equal(t, uint64(0), loadEpochs[2].Seq) 239 require.Equal(t, "1970-01-01 00:00:00 +0000 UTC", loadEpochs[2].StartTime.UTC().String()) 240 require.Equal(t, "1970-01-02 00:00:01 +0000 UTC", loadEpochs[2].EndTime.UTC().String()) 241 require.Equal(t, uint64(1), loadEpochs[3].Seq) 242 require.Equal(t, "1970-01-02 00:00:01 +0000 UTC", loadEpochs[3].StartTime.UTC().String()) 243 require.Equal(t, "1970-01-03 00:00:01 +0000 UTC", loadEpochs[3].ExpireTime.UTC().String()) 244 require.Equal(t, uint64(1), loadEpochs[4].Seq) 245 require.Equal(t, "1970-01-02 00:00:01 +0000 UTC", loadEpochs[4].StartTime.UTC().String()) 246 require.Equal(t, "1970-01-03 00:00:02 +0000 UTC", loadEpochs[4].EndTime.UTC().String()) 247 require.Equal(t, uint64(2), loadEpochs[5].Seq) 248 require.Equal(t, "1970-01-03 00:00:02 +0000 UTC", loadEpochs[5].StartTime.UTC().String()) 249 require.Equal(t, "1970-01-04 00:00:02 +0000 UTC", loadEpochs[5].ExpireTime.UTC().String()) 250 require.Equal(t, uint64(2), loadEpochs[6].Seq) 251 require.Equal(t, "1970-01-03 00:00:02 +0000 UTC", loadEpochs[6].StartTime.UTC().String()) 252 require.Equal(t, "1970-01-04 00:00:03 +0000 UTC", loadEpochs[6].EndTime.UTC().String()) 253 require.Equal(t, uint64(3), loadEpochs[7].Seq) 254 require.Equal(t, "1970-01-04 00:00:03 +0000 UTC", loadEpochs[7].StartTime.UTC().String()) 255 require.Equal(t, "1970-01-05 00:00:03 +0000 UTC", loadEpochs[7].ExpireTime.UTC().String()) 256 require.Equal(t, uint64(3), loadEpochs[8].Seq) 257 require.Equal(t, "1970-01-04 00:00:03 +0000 UTC", loadEpochs[8].StartTime.UTC().String()) 258 require.Equal(t, "1970-01-05 00:00:05 +0000 UTC", loadEpochs[8].EndTime.UTC().String()) 259 require.Equal(t, uint64(4), loadEpochs[9].Seq) 260 require.Equal(t, "1970-01-05 00:00:05 +0000 UTC", loadEpochs[9].StartTime.UTC().String()) 261 }