mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 21:26:42 +00:00
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 <4181434+jonastheis@users.noreply.github.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
This commit is contained in:
parent
8bee78a76c
commit
2ee0371685
7 changed files with 176 additions and 8 deletions
|
|
@ -227,8 +227,42 @@ func (s *SystemContract) VerifyUncles(chain consensus.ChainReader, block *types.
|
||||||
return nil
|
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 {
|
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
|
// 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
|
// we don't know when it will be sealed
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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)
|
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{
|
w.current = &work{
|
||||||
deadlineTimer: time.NewTimer(time.Until(deadline)),
|
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 {
|
func (w *worker) isCanonical(header *types.Header) bool {
|
||||||
return w.chain.GetBlockByNumber(header.Number.Uint64()).Hash() == header.Hash()
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1468,3 +1468,113 @@ func TestEuclidV2TransitionVerification(t *testing.T) {
|
||||||
_, err = chain.InsertChain(blocks)
|
_, err = chain.InsertChain(blocks)
|
||||||
assert.NoError(t, err)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -825,7 +825,7 @@ func (c *CliqueConfig) String() string {
|
||||||
// SystemContractConfig is the consensus engine configs for rollup sequencer sealing.
|
// SystemContractConfig is the consensus engine configs for rollup sequencer sealing.
|
||||||
type SystemContractConfig struct {
|
type SystemContractConfig struct {
|
||||||
Period uint64 `json:"period"` // Number of seconds between blocks to enforce
|
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
|
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
|
SystemContractSlot common.Hash `json:"system_contract_slot"` // slot of signer address in system contract on L1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 9 // Minor 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
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ go 1.22
|
||||||
replace github.com/scroll-tech/go-ethereum => ../../..
|
replace github.com/scroll-tech/go-ethereum => ../../..
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1
|
github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6
|
||||||
github.com/scroll-tech/go-ethereum v1.10.14-0.20250305151038-478940e79601
|
github.com/scroll-tech/go-ethereum v1.10.14-0.20250625112225-a67863c65587
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
gorm.io/driver/postgres v1.5.7
|
gorm.io/driver/postgres v1.5.7
|
||||||
|
|
|
||||||
|
|
@ -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/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 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.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 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE=
|
||||||
github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
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=
|
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue