diff --git a/common/aclock/aclock.go b/common/aclock/aclock.go new file mode 100644 index 0000000000..608941027d --- /dev/null +++ b/common/aclock/aclock.go @@ -0,0 +1,50 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package aclock contains an adjustable clock implementation. The time can be +// adjusted by adding an offset. +package aclock + +import ( + "errors" + "time" +) + +var offset time.Duration + +// AddOffset adds offset d to the current offset. d cannot be negative, and an +// error will be returned if the resulting offset overflows time.Duration. +func AddOffset(d time.Duration) (time.Duration, error) { + if d < 0 { + return 0, errors.New("aclock: duration for offset cannot be negative") + } + if offset+d < offset { + return 0, errors.New("aclock: offset overflow") + } + offset += d + return offset, nil +} + +// Now returns the current time with the offset applied. +func Now() time.Time { + return time.Now().Add(offset) +} + +// NowWithOffset returns the current time with the offset applied and the offset +// itself. +func NowWithOffset() (time.Time, time.Duration) { + return time.Now().Add(offset), offset +} diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index c79c30caed..3d293d042f 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/aclock" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" @@ -279,7 +280,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, number := header.Number.Uint64() // Don't waste time checking blocks from the future - if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { + if header.Time.Cmp(big.NewInt(aclock.Now().Unix())) > 0 { return consensus.ErrFutureBlock } // Checkpoint blocks need to enforce zero beneficiary @@ -571,8 +572,8 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro return consensus.ErrUnknownAncestor } header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period)) - if header.Time.Int64() < time.Now().Unix() { - header.Time = big.NewInt(time.Now().Unix()) + if header.Time.Int64() < aclock.Now().Unix() { + header.Time = big.NewInt(aclock.Now().Unix()) } return nil } @@ -637,7 +638,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c } } // Sweet, the protocol permits us to sign the block, wait for our time - delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple + delay := time.Unix(header.Time.Int64(), 0).Sub(aclock.Now()) // nolint: gosimple if header.Difficulty.Cmp(diffNoTurn) == 0 { // It's not our turn explicitly to sign, delay it a bit wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime diff --git a/core/blockchain.go b/core/blockchain.go index 156efe303a..6d555b9bc9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -43,7 +43,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru" ) var ( diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 26dc1e8a01..11b25ebfac 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/aclock" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus/ethash" @@ -1562,6 +1563,16 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) { api.b.SetHead(uint64(number)) } +// IncreaseTime increase the time offset for the clock used in consensus and +// mining. It has the effect of changing the timestamp of the next mined block. +func (api *PrivateDebugAPI) IncreaseTime(seconds uint64) (uint64, error) { + offset, err := aclock.AddOffset(time.Duration(seconds) * time.Second) + if err != nil { + return 0, err + } + return uint64(offset.Seconds()), nil +} + // PublicNetAPI offers network related RPC methods type PublicNetAPI struct { net *p2p.Server diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 6b98c8b7e1..299002b6bc 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -236,6 +236,11 @@ web3._extend({ call: 'debug_setHead', params: 1 }), + new web3._extend.Method({ + name: 'increaseTime', + call: 'debug_increaseTime', + params: 1 + }), new web3._extend.Method({ name: 'seedHash', call: 'debug_seedHash', diff --git a/miner/worker.go b/miner/worker.go index 48473796bc..63e698e605 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -26,6 +26,7 @@ import ( mapset "github.com/deckarep/golang-set" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/aclock" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" @@ -820,15 +821,19 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) w.mu.RLock() defer w.mu.RUnlock() - tstart := time.Now() + tstart, startOffset := aclock.NowWithOffset() parent := w.chain.CurrentBlock() if parent.Time().Cmp(new(big.Int).SetInt64(timestamp)) >= 0 { timestamp = parent.Time().Int64() + 1 } // this will ensure we're not going off too far in the future - if now := time.Now().Unix(); timestamp > now+1 { - wait := time.Duration(timestamp-now) * time.Second + now, nowOffset := aclock.NowWithOffset() + // nowWithOffset accounts for differences between the current aclock offset + // the offset when the current block was mined. + nowWithOffset := now.Unix() + int64(nowOffset.Seconds()-startOffset.Seconds()) + if timestamp > nowWithOffset+1 { + wait := time.Duration(timestamp-nowWithOffset) * time.Second log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) time.Sleep(wait) }