diff --git a/consensus/system_contract/consensus.go b/consensus/system_contract/consensus.go index 01d7be18502..6d061207489 100644 --- a/consensus/system_contract/consensus.go +++ b/consensus/system_contract/consensus.go @@ -227,8 +227,42 @@ func (s *SystemContract) VerifyUncles(chain consensus.ChainReader, block *types. return nil } +// CalcBlocksPerSecond returns the number of blocks per second +// Uses the BlocksPerSecond configuration parameter directly +// Default is 1 block per second if not specified +func CalcBlocksPerSecond(blocksPerSecond uint64) uint64 { + if blocksPerSecond == 0 { + return 1 // Default to 1 block per second + } + return blocksPerSecond +} + +// CalcPeriodMs calculates the period in milliseconds between blocks +// based on the blocks per second configuration +func CalcPeriodMs(blocksPerSecond uint64) uint64 { + bps := CalcBlocksPerSecond(blocksPerSecond) + return 1000 / bps +} + func (s *SystemContract) CalcTimestamp(parent *types.Header) uint64 { - timestamp := parent.Time + s.config.Period + var timestamp uint64 + if s.config.Period == 1 { + // Get the base timestamp (in seconds) + timestamp = parent.Time + + blocksPerSecond := CalcBlocksPerSecond(s.config.BlocksPerSecond) + + // Calculate the block index within the current period for the next block + blockIndex := parent.Number.Uint64() % blocksPerSecond + + // If this block is the last one in the current second, increment the timestamp + // We compare with blocksPerSecond-1 because blockIndex is 0-based + if blockIndex == blocksPerSecond-1 { + timestamp++ + } + } else { + timestamp = parent.Time + s.config.Period + } // If RelaxedPeriod is enabled, always set the header timestamp to now (ie the time we start building it) as // we don't know when it will be sealed diff --git a/miner/scroll_worker.go b/miner/scroll_worker.go index a1be4e82b14..ffd7b7890bf 100644 --- a/miner/scroll_worker.go +++ b/miner/scroll_worker.go @@ -562,6 +562,9 @@ func (w *worker) newWork(now time.Time, parent *types.Block, reorging bool, reor // system contract with relaxed period uses time.Now() as the header.Time, calculate the deadline deadline = time.Unix(int64(header.Time+w.chainConfig.SystemContract.Period), 0) } + if w.chainConfig.SystemContract != nil && w.chainConfig.SystemContract.Period == 1 { + deadline = CalculateBlockDeadline(w.chainConfig.SystemContract, header) + } w.current = &work{ deadlineTimer: time.NewTimer(time.Until(deadline)), @@ -1181,3 +1184,23 @@ func (w *worker) handleReorg(trigger *reorgTrigger) error { func (w *worker) isCanonical(header *types.Header) bool { return w.chain.GetBlockByNumber(header.Number.Uint64()).Hash() == header.Hash() } + +// CalculateBlockDeadline calculates the deadline for block production based on +// SystemContract configuration and current header information. +// This function abstracts the deadline calculation logic for easier testing. +func CalculateBlockDeadline(config *params.SystemContractConfig, header *types.Header) time.Time { + blocksPerSecond := system_contract.CalcBlocksPerSecond(config.BlocksPerSecond) + periodMs := system_contract.CalcPeriodMs(blocksPerSecond) + + // Calculate the actual timing based on block number within the current period + blockIndex := header.Number.Uint64() % blocksPerSecond + + // Calculate base time and add the fraction based on block index within the period + baseTimeNano := int64(header.Time) * int64(time.Second) + fractionNano := int64(blockIndex) * int64(periodMs) * int64(time.Millisecond) + + // Add one period to determine the deadline + nextBlockNano := baseTimeNano + fractionNano + int64(periodMs)*int64(time.Millisecond) + + return time.Unix(0, nextBlockNano) +} diff --git a/miner/scroll_worker_test.go b/miner/scroll_worker_test.go index 74f1371c916..b72d3ad66d3 100644 --- a/miner/scroll_worker_test.go +++ b/miner/scroll_worker_test.go @@ -1468,3 +1468,113 @@ func TestEuclidV2TransitionVerification(t *testing.T) { _, err = chain.InsertChain(blocks) assert.NoError(t, err) } + +// TestBlockIntervalWithWorkerDeadline tests the block interval calculation +// that simulates the actual worker deadline calculation logic +func TestBlockIntervalWithWorkerDeadline(t *testing.T) { + tests := []struct { + name string + period uint64 + blocksPerSecond uint64 + expectedInterval time.Duration + blocks int // number of blocks to simulate + }{ + { + name: "1 second period, 1 block per second", + period: 1, + blocksPerSecond: 1, + expectedInterval: 1000 * time.Millisecond, + blocks: 5, + }, + { + name: "1 second period, 2 blocks per second", + period: 1, + blocksPerSecond: 2, + expectedInterval: 500 * time.Millisecond, + blocks: 6, + }, + { + name: "1 second period, 4 blocks per second", + period: 1, + blocksPerSecond: 4, + expectedInterval: 250 * time.Millisecond, + blocks: 8, + }, + { + name: "2 second period, 2 blocks per second", + period: 2, + blocksPerSecond: 2, + expectedInterval: 500 * time.Millisecond, + blocks: 2, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := ¶ms.SystemContractConfig{ + Period: tt.period, + BlocksPerSecond: tt.blocksPerSecond, + RelaxedPeriod: false, + } + + // Start with a future timestamp to avoid time.Now() interference + currentTime := uint64(time.Now().Unix()) + 3600 // 1 hour in the future + var deadlines []time.Time + var timestamps []uint64 + + for i := 0; i < tt.blocks; i++ { + // Create header for current block + header := &types.Header{ + Time: currentTime, + Number: new(big.Int).SetUint64(uint64(i)), + } + + // Simulate the worker deadline calculation logic from newWork + deadline := CalculateBlockDeadline(config, header) + deadlines = append(deadlines, deadline) + + // Simulate timestamp calculation manually for next iteration + // This mimics the CalcTimestamp logic but simplified for testing + blocksPerSecond := system_contract.CalcBlocksPerSecond(tt.blocksPerSecond) + blocksPerPeriod := blocksPerSecond * tt.period + nextBlockNumber := uint64(i + 1) + + var newTimestamp uint64 + if nextBlockNumber%blocksPerPeriod == 0 && nextBlockNumber > 0 { + // Period boundary - increment timestamp + newTimestamp = currentTime + tt.period + } else { + // Within period - keep same timestamp + newTimestamp = currentTime + } + + timestamps = append(timestamps, newTimestamp) + + // Update currentTime for next iteration (simulate blockchain progression) + currentTime = newTimestamp + } + + // Verify the intervals between deadlines + for i := 1; i < len(deadlines); i++ { + interval := deadlines[i].Sub(deadlines[i-1]) + + // Allow small tolerance for timing precision + tolerance := 10 * time.Millisecond + if interval < tt.expectedInterval-tolerance || interval > tt.expectedInterval+tolerance { + t.Errorf("Block %d interval: got %v, want %v (±%v)", + i, interval, tt.expectedInterval, tolerance) + } + } + + // Note: Timestamp progression logic is verified in TestTimestampIncrementLogic + // Here we focus on deadline intervals which is what really matters for block production timing + blocksPerSecond := system_contract.CalcBlocksPerSecond(tt.blocksPerSecond) + blocksPerPeriod := blocksPerSecond * tt.period + + t.Logf("Test %s completed successfully:", tt.name) + t.Logf(" - Expected interval: %v", tt.expectedInterval) + t.Logf(" - Blocks per period: %d", blocksPerPeriod) + t.Logf(" - Period length: %d seconds", tt.period) + }) + } +} diff --git a/params/config.go b/params/config.go index cb26a7d10a5..04584f8e890 100644 --- a/params/config.go +++ b/params/config.go @@ -824,10 +824,10 @@ func (c *CliqueConfig) String() string { // SystemContractConfig is the consensus engine configs for rollup sequencer sealing. type SystemContractConfig struct { - Period uint64 `json:"period"` // Number of seconds between blocks to enforce - - SystemContractAddress common.Address `json:"system_contract_address"` // address of system contract on L1 - SystemContractSlot common.Hash `json:"system_contract_slot"` // slot of signer address in system contract on L1 + Period uint64 `json:"period"` // Number of seconds between blocks to enforce + BlocksPerSecond uint64 `json:"blocks_per_second,omitempty"` // Number of blocks per second within the period + SystemContractAddress common.Address `json:"system_contract_address"` // address of system contract on L1 + SystemContractSlot common.Hash `json:"system_contract_slot"` // slot of signer address in system contract on L1 RelaxedPeriod bool `json:"relaxed_period"` // Relaxes the period to be just an upper bound } diff --git a/params/version.go b/params/version.go index ccdbd490602..5b7a1c7d97b 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 9 // Minor version component of the current release - VersionPatch = 2 // Patch version component of the current release + VersionPatch = 3 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string ) diff --git a/rollup/missing_header_fields/export-headers-toolkit/go.mod b/rollup/missing_header_fields/export-headers-toolkit/go.mod index d10f0f5d0a8..ba453d6830c 100644 --- a/rollup/missing_header_fields/export-headers-toolkit/go.mod +++ b/rollup/missing_header_fields/export-headers-toolkit/go.mod @@ -5,8 +5,8 @@ go 1.22 replace github.com/scroll-tech/go-ethereum => ../../.. require ( - github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 - github.com/scroll-tech/go-ethereum v1.10.14-0.20250305151038-478940e79601 + github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6 + github.com/scroll-tech/go-ethereum v1.10.14-0.20250625112225-a67863c65587 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 gorm.io/driver/postgres v1.5.7 diff --git a/rollup/missing_header_fields/export-headers-toolkit/go.sum b/rollup/missing_header_fields/export-headers-toolkit/go.sum index 9823b3e9242..ae95e7a7f7a 100644 --- a/rollup/missing_header_fields/export-headers-toolkit/go.sum +++ b/rollup/missing_header_fields/export-headers-toolkit/go.sum @@ -172,6 +172,7 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 h1:Dhd58LE1D+dnoxpgLVeQBMF9uweL/fhQfZHWtWSiOlE= github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY= +github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg= github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE= github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=