Skip to content

Commit

Permalink
support millisecond-level timestamps for L1 block
Browse files Browse the repository at this point in the history
  • Loading branch information
flywukong committed Mar 3, 2025
1 parent 8520a2f commit a717b8c
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 20 deletions.
4 changes: 2 additions & 2 deletions op-node/rollup/derive/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex

// Sanity check the L1 origin was correctly selected to maintain the time invariant between L1 and L2
nextL2MilliTime := l2Parent.MillisecondTimestamp() + ba.rollupCfg.BlockTime
if nextL2MilliTime < l1Info.MilliTime() {
if nextL2MilliTime < l1Info.MillTimestamp() {
return nil, NewResetError(fmt.Errorf("cannot build L2 block on top %s for time %d before L1 origin %s at time %d",
l2Parent, nextL2MilliTime, eth.ToBlockID(l1Info), l1Info.MilliTime()))
l2Parent, nextL2MilliTime, eth.ToBlockID(l1Info), l1Info.MillTimestamp()))
}

var upgradeTxs []hexutil.Bytes
Expand Down
10 changes: 8 additions & 2 deletions op-node/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ import (
opflags "github.com/ethereum-optimism/optimism/op-service/flags"
)

const (
MinBlockTimeSeconds = 1
MaxBlockTimeSeconds = 3
MaxBlockTimeMs = 750
)

// NewConfig creates a Config from the provided flags or environment variables.
func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
if err := flags.CheckRequired(ctx); err != nil {
Expand All @@ -44,11 +50,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
}

{
if rollupConfig.BlockTime >= 1 && rollupConfig.BlockTime <= 3 {
if rollupConfig.BlockTime >= MinBlockTimeSeconds && rollupConfig.BlockTime <= MaxBlockTimeSeconds {
// Convert legacy second-level timestamp to millisecond timestamp,
// This is a compatibility behavior.
rollupConfig.BlockTime = rollupConfig.BlockTime * 1000
} else if rollupConfig.BlockTime%50 != 0 && rollupConfig.BlockTime > 750 {
} else if rollupConfig.BlockTime%50 != 0 && rollupConfig.BlockTime > MaxBlockTimeMs {
return nil, fmt.Errorf("block time is invalid, block_time: %v", rollupConfig.BlockTime)
}
// rollupConfig.BlockTime is millisecond block interval
Expand Down
29 changes: 22 additions & 7 deletions op-service/eth/block_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)

type BlockInfo interface {
Expand All @@ -16,7 +17,8 @@ type BlockInfo interface {
Root() common.Hash // state-root
NumberU64() uint64
Time() uint64
MilliTime() uint64
MillTimestamp() uint64
MillSeconds() uint64
// MixDigest field, reused for randomness after The Merge (Bellatrix hardfork)
MixDigest() common.Hash
BaseFee() *big.Int
Expand All @@ -39,6 +41,7 @@ func InfoToL1BlockRef(info BlockInfo) L1BlockRef {
Number: info.NumberU64(),
ParentHash: info.ParentHash(),
Time: info.Time(),
MsTime: info.MillSeconds(),
}
}

Expand Down Expand Up @@ -73,9 +76,15 @@ func (b blockInfo) ParentBeaconRoot() *common.Hash {
return b.Block.BeaconRoot()
}

func (b blockInfo) MilliTime() uint64 {
// TODO: adapt L1 timestamp
return b.Block.Time() * 1000
func (b blockInfo) MillTimestamp() uint64 {
return b.Block.Time()*1000 + b.MillSeconds()
}

func (b blockInfo) MillSeconds() uint64 {
if b.MixDigest() == (common.Hash{}) {
return 0
}
return uint256.NewInt(0).SetBytes32(b.MixDigest().Bytes()).Uint64()
}

func BlockToInfo(b *types.Block) BlockInfo {
Expand Down Expand Up @@ -108,9 +117,15 @@ func (h headerBlockInfo) Time() uint64 {
return h.Header.Time
}

func (h headerBlockInfo) MilliTime() uint64 {
// TODO: adapt L1 timestamp
return h.Header.Time * 1000
func (h headerBlockInfo) MillTimestamp() uint64 {
return h.Header.Time*1000 + h.MillSeconds()
}

func (h headerBlockInfo) MillSeconds() uint64 {
if h.MixDigest() == (common.Hash{}) {
return 0
}
return uint256.NewInt(0).SetBytes32(h.MixDigest().Bytes()).Uint64()
}

func (h headerBlockInfo) MixDigest() common.Hash {
Expand Down
11 changes: 10 additions & 1 deletion op-service/eth/heads.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/holiman/uint256"
)

// HeadSignalFn is used as callback function to accept head-signals
Expand Down Expand Up @@ -43,11 +45,18 @@ func WatchHeadChanges(ctx context.Context, src NewHeadSource, fn HeadSignalFn) (
for {
select {
case header := <-headChanges:
var mTime uint64
if header.MixDigest == (common.Hash{}) {
mTime = header.Time
} else {
mTime = uint256.NewInt(0).SetBytes32(header.MixDigest[:]).Uint64()
}
fn(eventsCtx, L1BlockRef{
Hash: header.Hash(),
Number: header.Number.Uint64(),
ParentHash: header.ParentHash,
Time: header.Time,
Time: mTime,
MsTime: mTime,
})
case <-eventsCtx.Done():
return nil
Expand Down
7 changes: 3 additions & 4 deletions op-service/eth/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,12 @@ type L1BlockRef struct {
Hash common.Hash `json:"hash"`
Number uint64 `json:"number"`
ParentHash common.Hash `json:"parentHash"`
Time uint64 `json:"timestamp"`
// TODO:
Time uint64 `json:"timestamp"` // second timestamp
MsTime uint64 `json:"msTimestamp"` // support millisecond
}

func (id L1BlockRef) MilliTimestamp() uint64 {
// TODO: adapt L1
return id.Time * 1000
return id.Time*1000 + id.MsTime
}

func (id L1BlockRef) String() string {
Expand Down
12 changes: 9 additions & 3 deletions op-service/sources/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,15 @@ func (h headerInfo) Time() uint64 {
return h.Header.Time
}

func (h headerInfo) MilliTime() uint64 {
// TODO: adapt L1 timestamp
return h.Header.Time * 1000
func (h headerInfo) MillTimestamp() uint64 {
return h.Header.Time*1000 + h.MillSeconds()
}

func (h headerInfo) MillSeconds() uint64 {
if h.MixDigest() == (common.Hash{}) {
return 0
}
return uint256.NewInt(0).SetBytes32(h.MixDigest().Bytes()).Uint64()
}

func (h headerInfo) MixDigest() common.Hash {
Expand Down
7 changes: 6 additions & 1 deletion op-service/testutils/l1info.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type MockBlockInfo struct {
InfoRoot common.Hash
InfoNum uint64
InfoTime uint64
InfoMTime uint64
InfoMixDigest [32]byte
InfoBaseFee *big.Int
InfoBlobBaseFee *big.Int
Expand Down Expand Up @@ -56,10 +57,14 @@ func (l *MockBlockInfo) Time() uint64 {
return l.InfoTime
}

func (l *MockBlockInfo) MilliTime() uint64 {
func (l *MockBlockInfo) MillTimestamp() uint64 {
return l.InfoTime * 1000
}

func (l *MockBlockInfo) MillSeconds() uint64 {
return l.InfoMTime
}

func (l *MockBlockInfo) MixDigest() common.Hash {
return l.InfoMixDigest
}
Expand Down

0 comments on commit a717b8c

Please sign in to comment.