Skip to content

Commit 2ee0371

Browse files
georgehaojonastheisThegaram
authored
SystemContract: support millisecond block generation (#1174)
* support ms block generation * update * bump version * deadline to common sense * remove unused code * add logs * update * update * update * add more logs * add tryCommitNewWork log * add more logs * rc10 * address comments * address comments * chore: auto version bump [bot] * update logic * fix CalcTimestamp * add debug log * address comments * update logic * fix lint * remove debug log * fix RelaxedPeriod * chore: auto version bump [bot] * update version * make relaxed_period omitempty * fix omitempty --------- Co-authored-by: jonastheis <[email protected]> Co-authored-by: Péter Garamvölgyi <[email protected]>
1 parent 8bee78a commit 2ee0371

File tree

7 files changed

+176
-8
lines changed

7 files changed

+176
-8
lines changed

consensus/system_contract/consensus.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,42 @@ func (s *SystemContract) VerifyUncles(chain consensus.ChainReader, block *types.
227227
return nil
228228
}
229229

230+
// CalcBlocksPerSecond returns the number of blocks per second
231+
// Uses the BlocksPerSecond configuration parameter directly
232+
// Default is 1 block per second if not specified
233+
func CalcBlocksPerSecond(blocksPerSecond uint64) uint64 {
234+
if blocksPerSecond == 0 {
235+
return 1 // Default to 1 block per second
236+
}
237+
return blocksPerSecond
238+
}
239+
240+
// CalcPeriodMs calculates the period in milliseconds between blocks
241+
// based on the blocks per second configuration
242+
func CalcPeriodMs(blocksPerSecond uint64) uint64 {
243+
bps := CalcBlocksPerSecond(blocksPerSecond)
244+
return 1000 / bps
245+
}
246+
230247
func (s *SystemContract) CalcTimestamp(parent *types.Header) uint64 {
231-
timestamp := parent.Time + s.config.Period
248+
var timestamp uint64
249+
if s.config.Period == 1 {
250+
// Get the base timestamp (in seconds)
251+
timestamp = parent.Time
252+
253+
blocksPerSecond := CalcBlocksPerSecond(s.config.BlocksPerSecond)
254+
255+
// Calculate the block index within the current period for the next block
256+
blockIndex := parent.Number.Uint64() % blocksPerSecond
257+
258+
// If this block is the last one in the current second, increment the timestamp
259+
// We compare with blocksPerSecond-1 because blockIndex is 0-based
260+
if blockIndex == blocksPerSecond-1 {
261+
timestamp++
262+
}
263+
} else {
264+
timestamp = parent.Time + s.config.Period
265+
}
232266

233267
// If RelaxedPeriod is enabled, always set the header timestamp to now (ie the time we start building it) as
234268
// we don't know when it will be sealed

miner/scroll_worker.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,9 @@ func (w *worker) newWork(now time.Time, parent *types.Block, reorging bool, reor
562562
// system contract with relaxed period uses time.Now() as the header.Time, calculate the deadline
563563
deadline = time.Unix(int64(header.Time+w.chainConfig.SystemContract.Period), 0)
564564
}
565+
if w.chainConfig.SystemContract != nil && w.chainConfig.SystemContract.Period == 1 {
566+
deadline = CalculateBlockDeadline(w.chainConfig.SystemContract, header)
567+
}
565568

566569
w.current = &work{
567570
deadlineTimer: time.NewTimer(time.Until(deadline)),
@@ -1181,3 +1184,23 @@ func (w *worker) handleReorg(trigger *reorgTrigger) error {
11811184
func (w *worker) isCanonical(header *types.Header) bool {
11821185
return w.chain.GetBlockByNumber(header.Number.Uint64()).Hash() == header.Hash()
11831186
}
1187+
1188+
// CalculateBlockDeadline calculates the deadline for block production based on
1189+
// SystemContract configuration and current header information.
1190+
// This function abstracts the deadline calculation logic for easier testing.
1191+
func CalculateBlockDeadline(config *params.SystemContractConfig, header *types.Header) time.Time {
1192+
blocksPerSecond := system_contract.CalcBlocksPerSecond(config.BlocksPerSecond)
1193+
periodMs := system_contract.CalcPeriodMs(blocksPerSecond)
1194+
1195+
// Calculate the actual timing based on block number within the current period
1196+
blockIndex := header.Number.Uint64() % blocksPerSecond
1197+
1198+
// Calculate base time and add the fraction based on block index within the period
1199+
baseTimeNano := int64(header.Time) * int64(time.Second)
1200+
fractionNano := int64(blockIndex) * int64(periodMs) * int64(time.Millisecond)
1201+
1202+
// Add one period to determine the deadline
1203+
nextBlockNano := baseTimeNano + fractionNano + int64(periodMs)*int64(time.Millisecond)
1204+
1205+
return time.Unix(0, nextBlockNano)
1206+
}

miner/scroll_worker_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,3 +1468,113 @@ func TestEuclidV2TransitionVerification(t *testing.T) {
14681468
_, err = chain.InsertChain(blocks)
14691469
assert.NoError(t, err)
14701470
}
1471+
1472+
// TestBlockIntervalWithWorkerDeadline tests the block interval calculation
1473+
// that simulates the actual worker deadline calculation logic
1474+
func TestBlockIntervalWithWorkerDeadline(t *testing.T) {
1475+
tests := []struct {
1476+
name string
1477+
period uint64
1478+
blocksPerSecond uint64
1479+
expectedInterval time.Duration
1480+
blocks int // number of blocks to simulate
1481+
}{
1482+
{
1483+
name: "1 second period, 1 block per second",
1484+
period: 1,
1485+
blocksPerSecond: 1,
1486+
expectedInterval: 1000 * time.Millisecond,
1487+
blocks: 5,
1488+
},
1489+
{
1490+
name: "1 second period, 2 blocks per second",
1491+
period: 1,
1492+
blocksPerSecond: 2,
1493+
expectedInterval: 500 * time.Millisecond,
1494+
blocks: 6,
1495+
},
1496+
{
1497+
name: "1 second period, 4 blocks per second",
1498+
period: 1,
1499+
blocksPerSecond: 4,
1500+
expectedInterval: 250 * time.Millisecond,
1501+
blocks: 8,
1502+
},
1503+
{
1504+
name: "2 second period, 2 blocks per second",
1505+
period: 2,
1506+
blocksPerSecond: 2,
1507+
expectedInterval: 500 * time.Millisecond,
1508+
blocks: 2,
1509+
},
1510+
}
1511+
1512+
for _, tt := range tests {
1513+
t.Run(tt.name, func(t *testing.T) {
1514+
config := &params.SystemContractConfig{
1515+
Period: tt.period,
1516+
BlocksPerSecond: tt.blocksPerSecond,
1517+
RelaxedPeriod: false,
1518+
}
1519+
1520+
// Start with a future timestamp to avoid time.Now() interference
1521+
currentTime := uint64(time.Now().Unix()) + 3600 // 1 hour in the future
1522+
var deadlines []time.Time
1523+
var timestamps []uint64
1524+
1525+
for i := 0; i < tt.blocks; i++ {
1526+
// Create header for current block
1527+
header := &types.Header{
1528+
Time: currentTime,
1529+
Number: new(big.Int).SetUint64(uint64(i)),
1530+
}
1531+
1532+
// Simulate the worker deadline calculation logic from newWork
1533+
deadline := CalculateBlockDeadline(config, header)
1534+
deadlines = append(deadlines, deadline)
1535+
1536+
// Simulate timestamp calculation manually for next iteration
1537+
// This mimics the CalcTimestamp logic but simplified for testing
1538+
blocksPerSecond := system_contract.CalcBlocksPerSecond(tt.blocksPerSecond)
1539+
blocksPerPeriod := blocksPerSecond * tt.period
1540+
nextBlockNumber := uint64(i + 1)
1541+
1542+
var newTimestamp uint64
1543+
if nextBlockNumber%blocksPerPeriod == 0 && nextBlockNumber > 0 {
1544+
// Period boundary - increment timestamp
1545+
newTimestamp = currentTime + tt.period
1546+
} else {
1547+
// Within period - keep same timestamp
1548+
newTimestamp = currentTime
1549+
}
1550+
1551+
timestamps = append(timestamps, newTimestamp)
1552+
1553+
// Update currentTime for next iteration (simulate blockchain progression)
1554+
currentTime = newTimestamp
1555+
}
1556+
1557+
// Verify the intervals between deadlines
1558+
for i := 1; i < len(deadlines); i++ {
1559+
interval := deadlines[i].Sub(deadlines[i-1])
1560+
1561+
// Allow small tolerance for timing precision
1562+
tolerance := 10 * time.Millisecond
1563+
if interval < tt.expectedInterval-tolerance || interval > tt.expectedInterval+tolerance {
1564+
t.Errorf("Block %d interval: got %v, want %v (±%v)",
1565+
i, interval, tt.expectedInterval, tolerance)
1566+
}
1567+
}
1568+
1569+
// Note: Timestamp progression logic is verified in TestTimestampIncrementLogic
1570+
// Here we focus on deadline intervals which is what really matters for block production timing
1571+
blocksPerSecond := system_contract.CalcBlocksPerSecond(tt.blocksPerSecond)
1572+
blocksPerPeriod := blocksPerSecond * tt.period
1573+
1574+
t.Logf("Test %s completed successfully:", tt.name)
1575+
t.Logf(" - Expected interval: %v", tt.expectedInterval)
1576+
t.Logf(" - Blocks per period: %d", blocksPerPeriod)
1577+
t.Logf(" - Period length: %d seconds", tt.period)
1578+
})
1579+
}
1580+
}

params/config.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -824,10 +824,10 @@ func (c *CliqueConfig) String() string {
824824

825825
// SystemContractConfig is the consensus engine configs for rollup sequencer sealing.
826826
type SystemContractConfig struct {
827-
Period uint64 `json:"period"` // Number of seconds between blocks to enforce
828-
829-
SystemContractAddress common.Address `json:"system_contract_address"` // address of system contract on L1
830-
SystemContractSlot common.Hash `json:"system_contract_slot"` // slot of signer address in system contract on L1
827+
Period uint64 `json:"period"` // Number of seconds between blocks to enforce
828+
BlocksPerSecond uint64 `json:"blocks_per_second,omitempty"` // Number of blocks per second within the period
829+
SystemContractAddress common.Address `json:"system_contract_address"` // address of system contract on L1
830+
SystemContractSlot common.Hash `json:"system_contract_slot"` // slot of signer address in system contract on L1
831831

832832
RelaxedPeriod bool `json:"relaxed_period"` // Relaxes the period to be just an upper bound
833833
}

params/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
const (
2525
VersionMajor = 5 // Major version component of the current release
2626
VersionMinor = 9 // Minor version component of the current release
27-
VersionPatch = 2 // Patch version component of the current release
27+
VersionPatch = 3 // Patch version component of the current release
2828
VersionMeta = "mainnet" // Version metadata to append to the version string
2929
)
3030

rollup/missing_header_fields/export-headers-toolkit/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ go 1.22
55
replace github.com/scroll-tech/go-ethereum => ../../..
66

77
require (
8-
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1
9-
github.com/scroll-tech/go-ethereum v1.10.14-0.20250305151038-478940e79601
8+
github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6
9+
github.com/scroll-tech/go-ethereum v1.10.14-0.20250625112225-a67863c65587
1010
github.com/spf13/cobra v1.9.1
1111
github.com/stretchr/testify v1.10.0
1212
gorm.io/driver/postgres v1.5.7

rollup/missing_header_fields/export-headers-toolkit/go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
172172
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
173173
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 h1:Dhd58LE1D+dnoxpgLVeQBMF9uweL/fhQfZHWtWSiOlE=
174174
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY=
175+
github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg=
175176
github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE=
176177
github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
177178
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=

0 commit comments

Comments
 (0)