common, consensus, miner: introduce debug_increaseTime RPC method

This commit is contained in:
Alex Browne 2019-01-29 15:04:25 -08:00
parent f4094d09cd
commit f268ec9b5f
No known key found for this signature in database
GPG key ID: CA6B8D4AFA783512
6 changed files with 80 additions and 8 deletions

50
common/aclock/aclock.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
// 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
}

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "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/hexutil"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc" "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() number := header.Number.Uint64()
// Don't waste time checking blocks from the future // 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 return consensus.ErrFutureBlock
} }
// Checkpoint blocks need to enforce zero beneficiary // 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 return consensus.ErrUnknownAncestor
} }
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period)) header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))
if header.Time.Int64() < time.Now().Unix() { if header.Time.Int64() < aclock.Now().Unix() {
header.Time = big.NewInt(time.Now().Unix()) header.Time = big.NewInt(aclock.Now().Unix())
} }
return nil 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 // 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 { if header.Difficulty.Cmp(diffNoTurn) == 0 {
// It's not our turn explicitly to sign, delay it a bit // It's not our turn explicitly to sign, delay it a bit
wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime

View file

@ -43,7 +43,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
) )
var ( var (

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common" "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/hexutil"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
@ -1562,6 +1563,16 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) {
api.b.SetHead(uint64(number)) 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 // PublicNetAPI offers network related RPC methods
type PublicNetAPI struct { type PublicNetAPI struct {
net *p2p.Server net *p2p.Server

View file

@ -236,6 +236,11 @@ web3._extend({
call: 'debug_setHead', call: 'debug_setHead',
params: 1 params: 1
}), }),
new web3._extend.Method({
name: 'increaseTime',
call: 'debug_increaseTime',
params: 1
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'seedHash', name: 'seedHash',
call: 'debug_seedHash', call: 'debug_seedHash',

View file

@ -26,6 +26,7 @@ import (
mapset "github.com/deckarep/golang-set" mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common" "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"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -820,15 +821,19 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
w.mu.RLock() w.mu.RLock()
defer w.mu.RUnlock() defer w.mu.RUnlock()
tstart := time.Now() tstart, startOffset := aclock.NowWithOffset()
parent := w.chain.CurrentBlock() parent := w.chain.CurrentBlock()
if parent.Time().Cmp(new(big.Int).SetInt64(timestamp)) >= 0 { if parent.Time().Cmp(new(big.Int).SetInt64(timestamp)) >= 0 {
timestamp = parent.Time().Int64() + 1 timestamp = parent.Time().Int64() + 1
} }
// this will ensure we're not going off too far in the future // this will ensure we're not going off too far in the future
if now := time.Now().Unix(); timestamp > now+1 { now, nowOffset := aclock.NowWithOffset()
wait := time.Duration(timestamp-now) * time.Second // 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)) log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait))
time.Sleep(wait) time.Sleep(wait)
} }