|
| 1 | +package system_contract |
| 2 | + |
| 3 | +import ( |
| 4 | + "math/big" |
| 5 | + "testing" |
| 6 | + "time" |
| 7 | + |
| 8 | + "github.com/scroll-tech/go-ethereum/core/types" |
| 9 | + "github.com/scroll-tech/go-ethereum/params" |
| 10 | +) |
| 11 | + |
| 12 | +func TestSystemContract_CalcTimestamp(t *testing.T) { |
| 13 | + // Use a future timestamp to avoid the "timestamp < time.Now()" condition |
| 14 | + futureTime := uint64(time.Now().Unix()) + 3600 // 1 hour in the future |
| 15 | + |
| 16 | + tests := []struct { |
| 17 | + name string |
| 18 | + period uint64 |
| 19 | + blocksPerSecond uint64 |
| 20 | + parentTime uint64 |
| 21 | + parentNumber uint64 |
| 22 | + expectedTime uint64 |
| 23 | + description string |
| 24 | + }{ |
| 25 | + { |
| 26 | + name: "1 second period, 1 block per second - first block", |
| 27 | + period: 1, |
| 28 | + blocksPerSecond: 1, |
| 29 | + parentTime: futureTime, |
| 30 | + parentNumber: 0, |
| 31 | + expectedTime: futureTime + 1, // Should increment by 1 second after 1 block |
| 32 | + description: "First block in period, should increment timestamp", |
| 33 | + }, |
| 34 | + { |
| 35 | + name: "1 second period, 2 blocks per second - first block", |
| 36 | + period: 1, |
| 37 | + blocksPerSecond: 2, |
| 38 | + parentTime: futureTime, |
| 39 | + parentNumber: 0, |
| 40 | + expectedTime: futureTime, // Should not increment yet (block 0, need block 1 to complete period) |
| 41 | + description: "First of two blocks in period, should not increment timestamp yet", |
| 42 | + }, |
| 43 | + { |
| 44 | + name: "1 second period, 2 blocks per second - second block", |
| 45 | + period: 1, |
| 46 | + blocksPerSecond: 2, |
| 47 | + parentTime: futureTime, |
| 48 | + parentNumber: 1, |
| 49 | + expectedTime: futureTime + 1, // Should increment by 1 second after 2 blocks |
| 50 | + description: "Second of two blocks in period, should increment timestamp", |
| 51 | + }, |
| 52 | + { |
| 53 | + name: "2 second period, 1 block per second - first block", |
| 54 | + period: 2, |
| 55 | + blocksPerSecond: 1, |
| 56 | + parentTime: futureTime, |
| 57 | + parentNumber: 0, |
| 58 | + expectedTime: futureTime, // Should not increment yet (need 2 blocks for 2-second period) |
| 59 | + description: "First of two blocks in 2-second period", |
| 60 | + }, |
| 61 | + { |
| 62 | + name: "2 second period, 1 block per second - second block", |
| 63 | + period: 2, |
| 64 | + blocksPerSecond: 1, |
| 65 | + parentTime: futureTime, |
| 66 | + parentNumber: 1, |
| 67 | + expectedTime: futureTime + 2, // Should increment by 2 seconds after 2 blocks |
| 68 | + description: "Second of two blocks in 2-second period, should increment by period", |
| 69 | + }, |
| 70 | + { |
| 71 | + name: "2 second period, 2 blocks per second - fourth block", |
| 72 | + period: 2, |
| 73 | + blocksPerSecond: 2, |
| 74 | + parentTime: futureTime, |
| 75 | + parentNumber: 3, // blocks 0,1,2,3 = 4 blocks total for 2-second period |
| 76 | + expectedTime: futureTime + 2, // Should increment by 2 seconds after 4 blocks |
| 77 | + description: "Last block in 2-second period with 2 blocks/sec", |
| 78 | + }, |
| 79 | + } |
| 80 | + |
| 81 | + for _, tt := range tests { |
| 82 | + t.Run(tt.name, func(t *testing.T) { |
| 83 | + config := ¶ms.SystemContractConfig{ |
| 84 | + Period: tt.period, |
| 85 | + BlocksPerSecond: tt.blocksPerSecond, |
| 86 | + RelaxedPeriod: false, // Ensure we test strict timing |
| 87 | + } |
| 88 | + |
| 89 | + sc := &SystemContract{ |
| 90 | + config: config, |
| 91 | + } |
| 92 | + |
| 93 | + parent := &types.Header{ |
| 94 | + Time: tt.parentTime, |
| 95 | + Number: new(big.Int).SetUint64(tt.parentNumber), |
| 96 | + } |
| 97 | + |
| 98 | + result := sc.CalcTimestamp(parent) |
| 99 | + if result != tt.expectedTime { |
| 100 | + t.Errorf("%s: CalcTimestamp() = %d, want %d\nDescription: %s\nConfig: Period=%d, BlocksPerSecond=%d, ParentNumber=%d", |
| 101 | + tt.name, result, tt.expectedTime, tt.description, tt.period, tt.blocksPerSecond, tt.parentNumber) |
| 102 | + } |
| 103 | + }) |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +func TestBlockIntervalTiming(t *testing.T) { |
| 108 | + tests := []struct { |
| 109 | + name string |
| 110 | + period uint64 |
| 111 | + blocksPerSecond uint64 |
| 112 | + expectedInterval time.Duration |
| 113 | + description string |
| 114 | + }{ |
| 115 | + { |
| 116 | + name: "1 block per second", |
| 117 | + period: 1, |
| 118 | + blocksPerSecond: 1, |
| 119 | + expectedInterval: 1000 * time.Millisecond, |
| 120 | + description: "Traditional 1 second block time", |
| 121 | + }, |
| 122 | + { |
| 123 | + name: "2 blocks per second", |
| 124 | + period: 1, |
| 125 | + blocksPerSecond: 2, |
| 126 | + expectedInterval: 500 * time.Millisecond, |
| 127 | + description: "Fast blocks every 500ms", |
| 128 | + }, |
| 129 | + { |
| 130 | + name: "4 blocks per second", |
| 131 | + period: 1, |
| 132 | + blocksPerSecond: 4, |
| 133 | + expectedInterval: 250 * time.Millisecond, |
| 134 | + description: "Very fast blocks every 250ms", |
| 135 | + }, |
| 136 | + { |
| 137 | + name: "10 blocks per second", |
| 138 | + period: 1, |
| 139 | + blocksPerSecond: 10, |
| 140 | + expectedInterval: 100 * time.Millisecond, |
| 141 | + description: "Ultra fast blocks every 100ms", |
| 142 | + }, |
| 143 | + } |
| 144 | + |
| 145 | + for _, tt := range tests { |
| 146 | + t.Run(tt.name, func(t *testing.T) { |
| 147 | + periodMs := CalcPeriodMs(tt.blocksPerSecond) |
| 148 | + actualInterval := time.Duration(periodMs) * time.Millisecond |
| 149 | + |
| 150 | + if actualInterval != tt.expectedInterval { |
| 151 | + t.Errorf("%s: Block interval = %v, want %v\nDescription: %s", |
| 152 | + tt.name, actualInterval, tt.expectedInterval, tt.description) |
| 153 | + } |
| 154 | + |
| 155 | + // Also verify the math: blocksPerSecond * interval should equal 1 second |
| 156 | + totalTimePerSecond := time.Duration(tt.blocksPerSecond) * actualInterval |
| 157 | + expectedTotalTime := 1 * time.Second |
| 158 | + |
| 159 | + if totalTimePerSecond != expectedTotalTime { |
| 160 | + t.Errorf("%s: %d blocks * %v interval = %v, want %v", |
| 161 | + tt.name, tt.blocksPerSecond, actualInterval, totalTimePerSecond, expectedTotalTime) |
| 162 | + } |
| 163 | + }) |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +func TestComplexTimingScenarios(t *testing.T) { |
| 168 | + // Test a more complex scenario: tracking timestamp progression over multiple blocks |
| 169 | + config := ¶ms.SystemContractConfig{ |
| 170 | + Period: 1, |
| 171 | + BlocksPerSecond: 4, // 250ms per block |
| 172 | + RelaxedPeriod: false, |
| 173 | + } |
| 174 | + |
| 175 | + sc := &SystemContract{ |
| 176 | + config: config, |
| 177 | + } |
| 178 | + |
| 179 | + baseTime := uint64(time.Now().Unix()) + 3600 // 1 hour in the future |
| 180 | + expectedTimestamps := []uint64{ |
| 181 | + baseTime, // block 0: no increment yet |
| 182 | + baseTime, // block 1: no increment yet |
| 183 | + baseTime, // block 2: no increment yet |
| 184 | + baseTime + 1, // block 3: complete period, increment by 1 second |
| 185 | + baseTime + 1, // block 4: start new period |
| 186 | + baseTime + 1, // block 5: continue period |
| 187 | + baseTime + 1, // block 6: continue period |
| 188 | + baseTime + 2, // block 7: complete second period, increment by 1 second |
| 189 | + } |
| 190 | + |
| 191 | + for i, expectedTime := range expectedTimestamps { |
| 192 | + parent := &types.Header{ |
| 193 | + Time: baseTime, |
| 194 | + Number: new(big.Int).SetUint64(uint64(i)), |
| 195 | + } |
| 196 | + |
| 197 | + result := sc.CalcTimestamp(parent) |
| 198 | + if result != expectedTime { |
| 199 | + t.Errorf("Block %d: CalcTimestamp() = %d, want %d", i, result, expectedTime) |
| 200 | + } |
| 201 | + |
| 202 | + // Update baseTime for next iteration to simulate time progression |
| 203 | + baseTime = result |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +func TestDefaultValues(t *testing.T) { |
| 208 | + // Test with default/zero values |
| 209 | + config := ¶ms.SystemContractConfig{ |
| 210 | + Period: 0, // Should default to 1 |
| 211 | + BlocksPerSecond: 0, // Should default to 1 |
| 212 | + RelaxedPeriod: false, |
| 213 | + } |
| 214 | + |
| 215 | + sc := &SystemContract{ |
| 216 | + config: config, |
| 217 | + } |
| 218 | + |
| 219 | + futureTime := uint64(time.Now().Unix()) + 3600 // 1 hour in the future |
| 220 | + parent := &types.Header{ |
| 221 | + Time: futureTime, |
| 222 | + Number: new(big.Int).SetUint64(0), |
| 223 | + } |
| 224 | + |
| 225 | + result := sc.CalcTimestamp(parent) |
| 226 | + // With defaults (period=1, blocksPerSecond=1), first block should increment timestamp |
| 227 | + expected := futureTime + 1 |
| 228 | + |
| 229 | + if result != expected { |
| 230 | + t.Errorf("With default values: CalcTimestamp() = %d, want %d", result, expected) |
| 231 | + } |
| 232 | + |
| 233 | + // Verify the period calculation |
| 234 | + periodMs := CalcPeriodMs(0) // Should default to 1000ms |
| 235 | + if periodMs != 1000 { |
| 236 | + t.Errorf("Default period calculation: got %d ms, want 1000 ms", periodMs) |
| 237 | + } |
| 238 | +} |
| 239 | + |
| 240 | +// TestTimestampIncrementLogic specifically tests the timestamp increment logic |
| 241 | +func TestTimestampIncrementLogic(t *testing.T) { |
| 242 | + config := ¶ms.SystemContractConfig{ |
| 243 | + Period: 1, |
| 244 | + BlocksPerSecond: 2, // 2 blocks per second |
| 245 | + RelaxedPeriod: false, |
| 246 | + } |
| 247 | + |
| 248 | + sc := &SystemContract{ |
| 249 | + config: config, |
| 250 | + } |
| 251 | + |
| 252 | + baseTime := uint64(time.Now().Unix()) + 3600 // 1 hour in the future |
| 253 | + currentTime := baseTime |
| 254 | + |
| 255 | + // Test for several blocks |
| 256 | + for i := 0; i < 6; i++ { |
| 257 | + parent := &types.Header{ |
| 258 | + Time: currentTime, |
| 259 | + Number: new(big.Int).SetUint64(uint64(i)), |
| 260 | + } |
| 261 | + |
| 262 | + newTimestamp := sc.CalcTimestamp(parent) |
| 263 | + nextBlockNumber := uint64(i + 1) |
| 264 | + |
| 265 | + // Calculate expected timestamp |
| 266 | + var expectedTimestamp uint64 |
| 267 | + if nextBlockNumber%2 == 0 { |
| 268 | + // This is a period boundary (blocks 2, 4, 6, ...) |
| 269 | + expectedTimestamp = currentTime + 1 |
| 270 | + } else { |
| 271 | + // This is within a period (blocks 1, 3, 5, ...) |
| 272 | + expectedTimestamp = currentTime |
| 273 | + } |
| 274 | + |
| 275 | + if newTimestamp != expectedTimestamp { |
| 276 | + t.Errorf("Block %d: got timestamp %d, want %d", nextBlockNumber, newTimestamp, expectedTimestamp) |
| 277 | + } |
| 278 | + |
| 279 | + // Update currentTime for next iteration (simulate blockchain progression) |
| 280 | + currentTime = newTimestamp |
| 281 | + } |
| 282 | +} |
0 commit comments