diff --git a/common/bidutil/bidutil.go b/common/bidutil/bidutil.go index 11ac39537e..20c42f1ccc 100644 --- a/common/bidutil/bidutil.go +++ b/common/bidutil/bidutil.go @@ -17,7 +17,7 @@ func BidBetterBefore(parentHeader *types.Header, blockPeriod uint64, delayLeftOv // BidMustBefore returns the time when the next bid must be received, // only considering the consensus delay but not bid simulation duration. func BidMustBefore(parentHeader *types.Header, blockPeriod uint64, delayLeftOver time.Duration) time.Time { - nextHeaderTime := time.UnixMilli(int64(parentHeader.TimeInMilliseconds() + blockPeriod)) + nextHeaderTime := time.UnixMilli(int64(parentHeader.TimeWithMilliseconds() + blockPeriod)) nextHeaderTime = nextHeaderTime.Add(-delayLeftOver) return nextHeaderTime } diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 31ce7d792d..3c3d3b3c13 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -56,6 +56,7 @@ const ( checkpointInterval = 1024 // Number of blocks after which to save the snapshot to the database defaultEpochLength = uint64(200) // Default number of blocks of checkpoint to update validatorSet from contract defaultBlockInterval = uint16(3000) // Default block interval in milliseconds + lorentzBlockInterval = uint16(800) // Block interval starting from the Lorentz hard fork defaultTurnLength = uint8(1) // Default consecutive number of blocks a validator receives priority for block production extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity @@ -637,8 +638,18 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot) } } else { - if header.ParentBeaconRoot == nil || *header.ParentBeaconRoot != (common.Hash{}) { - return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected zero hash", header.ParentBeaconRoot) + if header.ParentBeaconRoot == nil { + return fmt.Errorf("nil parentBeaconRoot after bohr hard fork") + } + lorentz := chain.Config().IsLorentz(header.Number, header.Time) + if !lorentz { + if *header.ParentBeaconRoot != (common.Hash{}) { // remove it once bsc mainnet passed lorentz hard fork for simplicity + return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected zero hash", header.ParentBeaconRoot) + } + } else { + if header.Milliseconds() >= 1000 { + return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected the last two bytes to represent milliseconds", header.ParentBeaconRoot) + } } } @@ -776,6 +787,10 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash // new snapshot snap = newSnapshot(p.config, p.signatures, number, blockHash, validators, voteAddrs, p.ethAPI) + if p.chainConfig.IsLorentz(checkpoint.Number, checkpoint.Time) { + snap.BlockInterval = lorentzBlockInterval + } + // get turnLength from headers and use that for new turnLength turnLength, err := parseTurnLength(checkpoint, p.chainConfig, p.config) if err != nil { @@ -1091,10 +1106,11 @@ func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header if parent == nil { return consensus.ErrUnknownAncestor } - header.Time = uint64(p.blockTimeForRamanujanFork(snap, header, parent).Seconds()) - if header.Time < uint64(time.Now().Unix()) { - header.Time = uint64(time.Now().Unix()) + blockTime := p.blockTimeForRamanujanFork(snap, header, parent) + if now := uint64(time.Now().UnixMilli()); blockTime < now { + blockTime = now } + header.SetTime(blockTime, p.chainConfig.IsLorentz(header.Number, header.Time)) header.Extra = header.Extra[:extraVanity-nextForkHashSize] nextForkHash := forkid.NextForkHash(p.chainConfig, p.genesisHash, chain.GenesisHeader().Time, number, header.Time) diff --git a/consensus/parlia/ramanujanfork.go b/consensus/parlia/ramanujanfork.go index 0dd6b4600a..4943e13832 100644 --- a/consensus/parlia/ramanujanfork.go +++ b/consensus/parlia/ramanujanfork.go @@ -26,17 +26,17 @@ func (p *Parlia) delayForRamanujanFork(snap *Snapshot, header *types.Header) tim return delay } -func (p *Parlia) blockTimeForRamanujanFork(snap *Snapshot, header, parent *types.Header) time.Duration { - blockTime := parent.TimeInMilliseconds() + uint64(snap.BlockInterval) +func (p *Parlia) blockTimeForRamanujanFork(snap *Snapshot, header, parent *types.Header) uint64 { + blockTime := parent.TimeWithMilliseconds() + uint64(snap.BlockInterval) if p.chainConfig.IsRamanujan(header.Number) { blockTime = blockTime + p.backOffTime(snap, header, p.val) } - return time.Duration(blockTime) * time.Millisecond + return blockTime } func (p *Parlia) blockTimeVerifyForRamanujanFork(snap *Snapshot, header, parent *types.Header) error { if p.chainConfig.IsRamanujan(header.Number) { - if header.TimeInMilliseconds() < parent.TimeInMilliseconds()+uint64(snap.BlockInterval)+p.backOffTime(snap, header, header.Coinbase) { + if header.TimeWithMilliseconds() < parent.TimeWithMilliseconds()+uint64(snap.BlockInterval)+p.backOffTime(snap, header, header.Coinbase) { return consensus.ErrFutureBlock } } diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 19f2fbdeb0..ad865225e1 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -316,7 +316,7 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea } // It's better to set it based on IsOnLorentz, but in practice, the effect is the same as using IsLorentz. if chainConfig.IsLorentz(header.Number, header.Time) { - s.BlockInterval = 800 + s.BlockInterval = lorentzBlockInterval } snap.Recents[number] = validator snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity]) diff --git a/core/chain_makers.go b/core/chain_makers.go index 9e6ff779f1..7b470a5f05 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -614,7 +614,7 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi header.ParentBeaconRoot = new(common.Hash) } else { header.WithdrawalsHash = &types.EmptyWithdrawalsHash - if cm.config.IsBohr(header.Number, header.Time) { + if cm.config.IsBohr(header.Number, header.Time) && !cm.config.IsLorentz(header.Number, header.Time) { header.ParentBeaconRoot = new(common.Hash) } if cm.config.IsPrague(header.Number, header.Time) { diff --git a/core/genesis.go b/core/genesis.go index 488e7fd421..880d058a8d 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -497,7 +497,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { // EIP-4788: The parentBeaconBlockRoot of the genesis block is always // the zero hash. This is because the genesis block does not have a parent // by definition. - if conf.Parlia == nil || conf.IsBohr(num, g.Timestamp) { + if conf.Parlia == nil || conf.IsBohr(num, g.Timestamp) && !conf.IsLorentz(num, g.Timestamp) { head.ParentBeaconRoot = new(common.Hash) } diff --git a/core/types/block.go b/core/types/block.go index dcbafe4337..1127f7bc72 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -30,6 +30,8 @@ import ( "golang.org/x/crypto/sha3" + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" @@ -156,8 +158,23 @@ func (h *Header) Hash() common.Hash { return rlpHash(h) } -func (h *Header) TimeInMilliseconds() uint64 { - return h.Time * 1000 +func (h *Header) Milliseconds() uint64 { + if h.ParentBeaconRoot == nil || *h.ParentBeaconRoot == (common.Hash{}) { + return 0 + } + return uint256.NewInt(0).SetBytes2(h.ParentBeaconRoot[30:]).Uint64() +} + +func (h *Header) TimeWithMilliseconds() uint64 { + return h.Time*1000 + h.Milliseconds() +} + +func (h *Header) SetTime(blockTime uint64, countMilliSeconds bool) { + h.Time = blockTime / 1000 + if countMilliSeconds { + milliseconds := common.Hash(uint256.NewInt(blockTime % 1000).Bytes32()) + h.ParentBeaconRoot = &milliseconds + } } var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size()) diff --git a/core/vote/vote_manager.go b/core/vote/vote_manager.go index 5a48745b86..71d9e1e7a5 100644 --- a/core/vote/vote_manager.go +++ b/core/vote/vote_manager.go @@ -148,7 +148,7 @@ func (voteManager *VoteManager) loop() { curHead := cHead.Header parentHeader := voteManager.chain.GetHeaderByHash(curHead.ParentHash) blockInterval, _ := voteManager.engine.BlockInterval(voteManager.chain, parentHeader) - nextBlockMinedTime := time.UnixMilli(int64((curHead.TimeInMilliseconds() + blockInterval))) + nextBlockMinedTime := time.UnixMilli(int64((curHead.TimeWithMilliseconds() + blockInterval))) timeForBroadcast := 50 * time.Millisecond // enough to broadcast a vote if time.Now().Add(timeForBroadcast).After(nextBlockMinedTime) { log.Warn("too late to vote", "Head.Time(Second)", curHead.Time, "Now(Millisecond)", time.Now().UnixMilli()) diff --git a/miner/worker.go b/miner/worker.go index b5fdc369bb..090560daa0 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1032,7 +1032,7 @@ func (w *worker) prepareWork(genParams *generateParams, witness bool) (*environm header.ParentBeaconRoot = genParams.beaconRoot } else { header.WithdrawalsHash = &types.EmptyWithdrawalsHash - if w.chainConfig.IsBohr(header.Number, header.Time) { + if w.chainConfig.IsBohr(header.Number, header.Time) && !w.chainConfig.IsLorentz(header.Number, header.Time) { header.ParentBeaconRoot = new(common.Hash) } if w.chainConfig.IsPrague(header.Number, header.Time) { diff --git a/params/config.go b/params/config.go index 09b8c4fe24..aaaf004fb3 100644 --- a/params/config.go +++ b/params/config.go @@ -596,8 +596,7 @@ func (c CliqueConfig) String() string { // ParliaConfig is the consensus engine configs for proof-of-staked-authority based sealing. type ParliaConfig struct { - DeprecatedPeriod uint64 `json:"period"` // Number of milliseconds between blocks to enforce; deprecated, existed just for load old `ParliaConfig` from disk - Epoch uint64 `json:"epoch"` // Epoch length to update validatorSet + Epoch uint64 `json:"epoch"` // Epoch length to update validatorSet } // String implements the stringer interface, returning the consensus engine details.