Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for transitions #1371

Merged
merged 8 commits into from
Apr 6, 2022
Merged
6 changes: 6 additions & 0 deletions cmd/geth/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ func initGenesis(ctx *cli.Context) error {
if err != nil {
utils.Fatalf("maxCodeSize data invalid: %v", err)
}
if genesis.Config.IsQuorum {
err = genesis.Config.CheckTransitionsData()
if err != nil {
utils.Fatalf("transitions data invalid: %v", err)
}
}
// End Quorum

// Open and initialise both full and light databases
Expand Down
14 changes: 8 additions & 6 deletions cmd/geth/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,9 +392,10 @@ func TestFlagsConfig(t *testing.T) {
// QUORUM
// [Eth.Istanbul]
quorumIstanbul := eth.Istanbul
assert.Equal(t, uint64(10000), quorumIstanbul.RequestTimeout)
assert.Equal(t, uint64(1), quorumIstanbul.BlockPeriod)
assert.Equal(t, uint64(30000), quorumIstanbul.Epoch)
config := quorumIstanbul.GetConfig(nil)
assert.Equal(t, uint64(10000), config.RequestTimeout)
assert.Equal(t, uint64(1), config.BlockPeriod)
assert.Equal(t, uint64(30000), config.Epoch)
assert.Equal(t, big.NewInt(0), quorumIstanbul.Ceil2Nby3Block)
assert.Equal(t, istanbul.RoundRobin, quorumIstanbul.ProposerPolicy.Id) // conflict with genesis?
// END QUORUM
Expand Down Expand Up @@ -685,9 +686,10 @@ func testConfig(t *testing.T, cfg *gethConfig) {
// QUORUM
// [Eth.Quorum.Istanbul]
istanbul := cfg.Eth.Istanbul
assert.Equal(t, uint64(10000), istanbul.RequestTimeout)
assert.Equal(t, uint64(5), istanbul.BlockPeriod)
assert.Equal(t, uint64(30000), istanbul.Epoch)
config := istanbul.GetConfig(nil)
assert.Equal(t, uint64(10000), config.RequestTimeout)
assert.Equal(t, uint64(5), config.BlockPeriod)
assert.Equal(t, uint64(30000), config.Epoch)
assert.Equal(t, big.NewInt(0), istanbul.Ceil2Nby3Block)
// END QUORUM
}
9 changes: 9 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2223,14 +2223,23 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readOnly bool, useExist bool)
istanbulConfig.ProposerPolicy = istanbul.NewProposerPolicy(istanbul.ProposerPolicyId(config.Istanbul.ProposerPolicy))
istanbulConfig.Ceil2Nby3Block = config.Istanbul.Ceil2Nby3Block
istanbulConfig.TestQBFTBlock = config.Istanbul.TestQBFTBlock
if config.Transitions != nil && len(config.Transitions) != 0 {
istanbulConfig.Transitions = config.Transitions
}
engine = istanbulBackend.New(istanbulConfig, stack.GetNodeKey(), chainDb)
} else if config.IBFT != nil {
ibftConfig := setBFTConfig(config.IBFT.BFTConfig)
ibftConfig.TestQBFTBlock = nil
if config.Transitions != nil && len(config.Transitions) != 0 {
ibftConfig.Transitions = config.Transitions
}
engine = istanbulBackend.New(ibftConfig, stack.GetNodeKey(), chainDb)
} else if config.QBFT != nil {
qbftConfig := setBFTConfig(config.QBFT.BFTConfig)
qbftConfig.TestQBFTBlock = big.NewInt(0)
if config.Transitions != nil && len(config.Transitions) != 0 {
qbftConfig.Transitions = config.Transitions
}
engine = istanbulBackend.New(qbftConfig, stack.GetNodeKey(), chainDb)
} else if config.IsQuorum {
// for Raft
Expand Down
5 changes: 3 additions & 2 deletions cmd/utils/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ func TestQuorumConfigFlags(t *testing.T) {
assert.Equal(t, 12*time.Second, arbitraryEthConfig.EVMCallTimeOut, "EVMCallTimeOut value is incorrect")
assert.Equal(t, true, arbitraryEthConfig.QuorumChainConfig.MultiTenantEnabled(), "MultitenancyFlag value is incorrect")
assert.Equal(t, true, arbitraryEthConfig.QuorumChainConfig.PrivacyMarkerEnabled(), "QuorumEnablePrivacyMarker value is incorrect")
assert.Equal(t, uint64(23), arbitraryEthConfig.Istanbul.RequestTimeout, "IstanbulRequestTimeoutFlag value is incorrect")
assert.Equal(t, uint64(34), arbitraryEthConfig.Istanbul.BlockPeriod, "IstanbulBlockPeriodFlag value is incorrect")
config := arbitraryEthConfig.Istanbul.GetConfig(nil)
assert.Equal(t, uint64(23), config.RequestTimeout, "IstanbulRequestTimeoutFlag value is incorrect")
assert.Equal(t, uint64(34), config.BlockPeriod, "IstanbulBlockPeriodFlag value is incorrect")
assert.Equal(t, true, arbitraryEthConfig.RaftMode, "RaftModeFlag value is incorrect")
}
4 changes: 2 additions & 2 deletions consensus/istanbul/backend/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ func (sb *Backend) snapshot(chain consensus.ChainHeaderReader, number uint64, ha
}
// If an on-disk checkpoint snapshot can be found, use that
if number%checkpointInterval == 0 {
if s, err := loadSnapshot(sb.config.Epoch, sb.db, hash); err == nil {
if s, err := loadSnapshot(sb.config.GetConfig(new(big.Int).SetUint64(number)).Epoch, sb.db, hash); err == nil {
snap = s
sb.snapLogger(snap).Trace("BFT: loaded voting snapshot from database")
break
Expand All @@ -367,7 +367,7 @@ func (sb *Backend) snapshot(chain consensus.ChainHeaderReader, number uint64, ha
return nil, err
}

snap = newSnapshot(sb.config.Epoch, 0, genesis.Hash(), validator.NewSet(validators, sb.config.ProposerPolicy))
snap = newSnapshot(sb.config.GetConfig(new(big.Int).SetUint64(number)).Epoch, 0, genesis.Hash(), validator.NewSet(validators, sb.config.ProposerPolicy))
if err := sb.storeSnap(snap); err != nil {
return nil, err
}
Expand Down
7 changes: 4 additions & 3 deletions consensus/istanbul/backend/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ func copyConfig(config *istanbul.Config) *istanbul.Config {
}

func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header {
blockNumber := parent.Number().Add(parent.Number(), common.Big1)
header := &types.Header{
ParentHash: parent.Hash(),
Number: parent.Number().Add(parent.Number(), common.Big1),
Number: blockNumber,
GasLimit: core.CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()),
GasUsed: 0,
Time: parent.Time() + config.BlockPeriod,
Time: parent.Time() + config.GetConfig(blockNumber).BlockPeriod,
Difficulty: istanbulcommon.DefaultDifficulty,
}
return header
Expand Down Expand Up @@ -310,7 +311,7 @@ func TestVerifyHeader(t *testing.T) {
// invalid timestamp
block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
header = block.Header()
header.Time = chain.Genesis().Time() + (engine.config.BlockPeriod - 1)
header.Time = chain.Genesis().Time() + (engine.config.GetConfig(block.Number()).BlockPeriod - 1)
err = engine.VerifyHeader(chain, header, false)
if err != istanbulcommon.ErrInvalidTimestamp {
t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidTimestamp)
Expand Down
5 changes: 3 additions & 2 deletions consensus/istanbul/backend/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,10 @@ func TestVoting(t *testing.T) {
// Assemble a chain of headers from the cast votes
headers := make([]*types.Header, len(tt.votes))
for j, vote := range tt.votes {
blockNumber := big.NewInt(int64(j) + 1)
headers[j] = &types.Header{
Number: big.NewInt(int64(j) + 1),
Time: uint64(int64(j) * int64(config.BlockPeriod)),
Number: blockNumber,
Time: uint64(int64(j) * int64(config.GetConfig(blockNumber).BlockPeriod)),
Coinbase: accounts.address(vote.validator),
Difficulty: istanbulcommon.DefaultDifficulty,
MixDigest: types.IstanbulDigest,
Expand Down
38 changes: 29 additions & 9 deletions consensus/istanbul/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"math/big"
"sync"

"github.com/ethereum/go-ethereum/params"
"github.com/naoina/toml"
)

Expand Down Expand Up @@ -125,6 +126,7 @@ type Config struct {
Ceil2Nby3Block *big.Int `toml:",omitempty"` // Number of confirmations required to move from one state to next [2F + 1 to Ceil(2N/3)]
AllowedFutureBlockTime uint64 `toml:",omitempty"` // Max time (in seconds) from current time allowed for blocks, before they're considered future blocks
TestQBFTBlock *big.Int `toml:",omitempty"` // Fork block at which block confirmations are done using qbft consensus instead of ibft
Transitions []params.Transition
}

var DefaultConfig = &Config{
Expand All @@ -147,17 +149,35 @@ func (c Config) QBFTBlockNumber() int64 {

// IsQBFTConsensusAt checks if qbft consensus is enabled for the block height identified by the given header
func (c *Config) IsQBFTConsensusAt(blockNumber *big.Int) bool {
// If qbftBlock is not defined in genesis qbft consensus is not used
if c.TestQBFTBlock == nil {
return false
if c.TestQBFTBlock != nil {
baptiste-b-pegasys marked this conversation as resolved.
Show resolved Hide resolved
if c.TestQBFTBlock.Uint64() == 0 {
return true
}

if blockNumber.Cmp(c.TestQBFTBlock) >= 0 {
return true
}
}

if c.TestQBFTBlock.Uint64() == 0 {
return true
for i := 0; c.Transitions != nil && i < len(c.Transitions) && c.Transitions[i].Block.Cmp(blockNumber) <= 0; i++ {
baptiste-b-pegasys marked this conversation as resolved.
Show resolved Hide resolved
if c.Transitions[i].Algorithm == params.QBFT {
return true
}
}
return false
}

if blockNumber.Cmp(c.TestQBFTBlock) >= 0 {
return true
func (c Config) GetConfig(blockNumber *big.Int) Config {
newConfig := c
for i := 0; c.Transitions != nil && i < len(c.Transitions) && c.Transitions[i].Block.Cmp(blockNumber) <= 0; i++ {
baptiste-b-pegasys marked this conversation as resolved.
Show resolved Hide resolved
if c.Transitions[i].RequestTimeoutSeconds != 0 {
newConfig.RequestTimeout = c.Transitions[i].RequestTimeoutSeconds
}
if c.Transitions[i].EpochLength != 0 {
newConfig.Epoch = c.Transitions[i].EpochLength
}
if c.Transitions[i].BlockPeriodSeconds != 0 {
newConfig.BlockPeriod = c.Transitions[i].BlockPeriodSeconds
}
}
return false
return newConfig
}
84 changes: 84 additions & 0 deletions consensus/istanbul/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
package istanbul

import (
"math/big"
"reflect"
"testing"

"github.com/ethereum/go-ethereum/params"
"github.com/naoina/toml"
"github.com/stretchr/testify/assert"
)
Expand All @@ -42,3 +45,84 @@ func TestProposerPolicy_MarshalTOML(t *testing.T) {
}
assert.Equal(t, output, b, "ProposerPolicy MarshalTOML mismatch")
}

func TestGetConfig(t *testing.T) {
if !reflect.DeepEqual(DefaultConfig.GetConfig(nil), *DefaultConfig) {
t.Errorf("error default config:\nexpected: %v\n", DefaultConfig)
}

config := DefaultConfig
config.Transitions = []params.Transition{{
Block: big.NewInt(1),
EpochLength: 40000,
}, {
Block: big.NewInt(3),
BlockPeriodSeconds: 5,
}, {
Block: big.NewInt(5),
RequestTimeoutSeconds: 15000,
}}
config1 := *DefaultConfig
config1.Epoch = 40000
config3 := config1
config3.BlockPeriod = 5
config5 := config3
config5.RequestTimeout = 15000

type test struct {
blockNumber int64
expectedConfig Config
}
tests := []test{
{1, config1},
{2, config1},
{3, config3},
{4, config3},
{5, config5},
{10, config5},
{100, config5},
}

for _, test := range tests {
c := config.GetConfig(big.NewInt(test.blockNumber))
if !reflect.DeepEqual(c, test.expectedConfig) {
t.Errorf("error mismatch:\nexpected: %v\ngot: %v\n", test.expectedConfig, c)
}
}
}

func TestIsQBFTConsensusAt(t *testing.T) {
config1 := *DefaultConfig
config1.TestQBFTBlock = nil
config2 := *DefaultConfig
config2.TestQBFTBlock = big.NewInt(5)
config3 := *DefaultConfig
config3.TestQBFTBlock = nil
config3.Transitions = []params.Transition{
{Block: big.NewInt(10), Algorithm: params.QBFT},
}
type test struct {
config Config
blockNumber int64
isQBFT bool
}
tests := []test{
{*DefaultConfig, 0, true},
{*DefaultConfig, 10, true},
{config1, 0, false},
{config1, 10, false},
{config2, 4, false},
{config2, 5, true},
{config2, 7, true},
{config3, 0, false},
{config3, 7, false},
{config3, 10, true},
{config3, 11, true},
}
for _, test := range tests {
isQbft := test.config.IsQBFTConsensusAt(big.NewInt(test.blockNumber))
if !reflect.DeepEqual(isQbft, test.isQBFT) {
t.Errorf("error mismatch:\nexpected: %v\ngot: %v\n", test.isQBFT, isQbft)
}
}
}
2 changes: 1 addition & 1 deletion consensus/istanbul/ibft/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ func (c *core) newRoundChangeTimer() {
c.stopTimer()

// set timeout based on the round number
timeout := time.Duration(c.config.RequestTimeout) * time.Millisecond
timeout := time.Duration(c.config.GetConfig(c.current.Sequence()).RequestTimeout) * time.Millisecond
round := c.current.Round().Uint64()
if round > 0 {
timeout += time.Duration(math.Pow(2, float64(round))) * time.Second
Expand Down
4 changes: 2 additions & 2 deletions consensus/istanbul/ibft/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (e *Engine) verifyCascadingFields(chain consensus.ChainHeaderReader, header
}

// Ensure that the block's timestamp isn't too close to it's parent
if parent.Time+e.cfg.BlockPeriod > header.Time {
if parent.Time+e.cfg.GetConfig(parent.Number).BlockPeriod > header.Time {
return istanbulcommon.ErrInvalidTimestamp
}

Expand Down Expand Up @@ -284,7 +284,7 @@ func (e *Engine) Prepare(chain consensus.ChainHeaderReader, header *types.Header
header.Extra = extra

// set header's timestamp
header.Time = parent.Time + e.cfg.BlockPeriod
header.Time = parent.Time + e.cfg.GetConfig(header.Number).BlockPeriod
if header.Time < uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
}
Expand Down
2 changes: 1 addition & 1 deletion consensus/istanbul/qbft/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func (c *core) newRoundChangeTimer() {
c.stopTimer()

// set timeout based on the round number
baseTimeout := time.Duration(c.config.RequestTimeout) * time.Millisecond
baseTimeout := time.Duration(c.config.GetConfig(c.current.Sequence()).RequestTimeout) * time.Millisecond
round := c.current.Round().Uint64()

timeout := baseTimeout * time.Duration(math.Pow(2, float64(round)))
Expand Down
4 changes: 2 additions & 2 deletions consensus/istanbul/qbft/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (e *Engine) verifyCascadingFields(chain consensus.ChainHeaderReader, header
}

// Ensure that the block's timestamp isn't too close to it's parent
if parent.Time+e.cfg.BlockPeriod > header.Time {
if parent.Time+e.cfg.GetConfig(parent.Number).BlockPeriod > header.Time {
return istanbulcommon.ErrInvalidTimestamp
}

Expand Down Expand Up @@ -316,7 +316,7 @@ func (e *Engine) Prepare(chain consensus.ChainHeaderReader, header *types.Header
header.Difficulty = istanbulcommon.DefaultDifficulty

// set header's timestamp
header.Time = parent.Time + e.cfg.BlockPeriod
header.Time = parent.Time + e.cfg.GetConfig(header.Number).BlockPeriod
if header.Time < uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
}
Expand Down
3 changes: 3 additions & 0 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, co
chainConfig.Clique.AllowedFutureBlockTime = config.Miner.AllowedFutureBlockTime //Quorum
return clique.New(chainConfig.Clique, db)
}
if chainConfig.Transitions != nil && len(chainConfig.Transitions) != 0 {
config.Istanbul.Transitions = chainConfig.Transitions
}
// If Istanbul is requested, set it up
if chainConfig.Istanbul != nil {
log.Warn("WARNING: The attribute config.istanbul is deprecated and will be removed in the future, please use config.ibft on genesis file")
Expand Down
Loading