123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- // Copyright 2017 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 istanbul
- import (
- "math/big"
- "strings"
- "sync"
- "github.com/ethereum/go-ethereum/accounts/abi/bind"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/params"
- "github.com/naoina/toml"
- )
- type ProposerPolicyId uint64
- const (
- RoundRobin ProposerPolicyId = iota
- Sticky
- )
- // ProposerPolicy represents the Validator Proposer Policy
- type ProposerPolicy struct {
- Id ProposerPolicyId // Could be RoundRobin or Sticky
- By ValidatorSortByFunc // func that defines how the ValidatorSet should be sorted
- registry []ValidatorSet // Holds the ValidatorSet for a given block height
- registryMU *sync.Mutex // Mutex to lock access to changes to Registry
- }
- // NewRoundRobinProposerPolicy returns a RoundRobin ProposerPolicy with ValidatorSortByString as default sort function
- func NewRoundRobinProposerPolicy() *ProposerPolicy {
- return NewProposerPolicy(RoundRobin)
- }
- // NewStickyProposerPolicy return a Sticky ProposerPolicy with ValidatorSortByString as default sort function
- func NewStickyProposerPolicy() *ProposerPolicy {
- return NewProposerPolicy(Sticky)
- }
- func NewProposerPolicy(id ProposerPolicyId) *ProposerPolicy {
- return NewProposerPolicyByIdAndSortFunc(id, ValidatorSortByString())
- }
- func NewProposerPolicyByIdAndSortFunc(id ProposerPolicyId, by ValidatorSortByFunc) *ProposerPolicy {
- return &ProposerPolicy{Id: id, By: by, registryMU: new(sync.Mutex)}
- }
- type proposerPolicyToml struct {
- Id ProposerPolicyId
- }
- func (p *ProposerPolicy) MarshalTOML() (interface{}, error) {
- if p == nil {
- return nil, nil
- }
- pp := &proposerPolicyToml{Id: p.Id}
- data, err := toml.Marshal(pp)
- if err != nil {
- return nil, err
- }
- return string(data), nil
- }
- func (p *ProposerPolicy) UnmarshalTOML(decode func(interface{}) error) error {
- var innerToml string
- err := decode(&innerToml)
- if err != nil {
- return err
- }
- var pp proposerPolicyToml
- err = toml.Unmarshal([]byte(innerToml), &pp)
- if err != nil {
- return err
- }
- p.Id = pp.Id
- p.By = ValidatorSortByString()
- return nil
- }
- // Use sets the ValidatorSortByFunc for the given ProposerPolicy and sorts the validatorSets according to it
- func (p *ProposerPolicy) Use(v ValidatorSortByFunc) {
- p.By = v
- for _, validatorSet := range p.registry {
- validatorSet.SortValidators()
- }
- }
- // RegisterValidatorSet stores the given ValidatorSet in the policy registry
- func (p *ProposerPolicy) RegisterValidatorSet(valSet ValidatorSet) {
- p.registryMU.Lock()
- defer p.registryMU.Unlock()
- if len(p.registry) == 0 {
- p.registry = []ValidatorSet{valSet}
- } else {
- p.registry = append(p.registry, valSet)
- }
- }
- // ClearRegistry removes any ValidatorSet from the ProposerPolicy registry
- func (p *ProposerPolicy) ClearRegistry() {
- p.registryMU.Lock()
- defer p.registryMU.Unlock()
- p.registry = nil
- }
- type Config struct {
- RequestTimeout uint64 `toml:",omitempty"` // The timeout for each Istanbul round in milliseconds.
- BlockPeriod uint64 `toml:",omitempty"` // Default minimum difference between two consecutive block's timestamps in second
- EmptyBlockPeriod uint64 `toml:",omitempty"` // Default minimum difference between a block and empty block's timestamps in second
- ProposerPolicy *ProposerPolicy `toml:",omitempty"` // The policy for proposer selection
- Epoch uint64 `toml:",omitempty"` // The number of blocks after which to checkpoint and reset the pending votes
- 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
- BeneficiaryMode *string `toml:",omitempty"` // Mode for setting the beneficiary, either: list, besu, validators (beneficiary list is the list of validators)
- BlockReward *math.HexOrDecimal256 `toml:",omitempty"` // Reward
- MiningBeneficiary *common.Address `toml:",omitempty"` // Wallet address that benefits at every new block (besu mode)
- ValidatorContract common.Address `toml:",omitempty"`
- Validators []common.Address `toml:",omitempty"`
- ValidatorSelectionMode *string `toml:",omitempty"`
- Client bind.ContractCaller `toml:",omitempty"`
- Transitions []params.Transition
- }
- var DefaultConfig = &Config{
- RequestTimeout: 10000,
- BlockPeriod: 5,
- EmptyBlockPeriod: 0,
- ProposerPolicy: NewRoundRobinProposerPolicy(),
- Epoch: 30000,
- Ceil2Nby3Block: big.NewInt(0),
- AllowedFutureBlockTime: 0,
- TestQBFTBlock: big.NewInt(0),
- }
- // QBFTBlockNumber returns the qbftBlock fork block number, returns -1 if qbftBlock is not defined
- func (c Config) QBFTBlockNumber() int64 {
- if c.TestQBFTBlock == nil {
- return -1
- }
- return c.TestQBFTBlock.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 c.TestQBFTBlock != nil {
- if c.TestQBFTBlock.Uint64() == 0 {
- return true
- }
- if blockNumber.Cmp(c.TestQBFTBlock) >= 0 {
- return true
- }
- }
- result := false
- if blockNumber == nil {
- blockNumber = big.NewInt(0)
- }
- c.getTransitionValue(blockNumber, func(t params.Transition) {
- if strings.EqualFold(t.Algorithm, params.QBFT) {
- result = true
- }
- })
- return result
- }
- func (c Config) GetConfig(blockNumber *big.Int) Config {
- newConfig := c
- c.getTransitionValue(blockNumber, func(transition params.Transition) {
- if transition.RequestTimeoutSeconds != 0 {
- newConfig.RequestTimeout = transition.RequestTimeoutSeconds
- }
- if transition.EpochLength != 0 {
- newConfig.Epoch = transition.EpochLength
- }
- if transition.BlockPeriodSeconds != 0 {
- newConfig.BlockPeriod = transition.BlockPeriodSeconds
- }
- if transition.EmptyBlockPeriodSeconds != nil {
- newConfig.EmptyBlockPeriod = *transition.EmptyBlockPeriodSeconds
- }
- if transition.BeneficiaryMode != nil {
- newConfig.BeneficiaryMode = transition.BeneficiaryMode
- }
- if transition.BlockReward != nil {
- newConfig.BlockReward = transition.BlockReward
- }
- if transition.MiningBeneficiary != nil {
- newConfig.MiningBeneficiary = transition.MiningBeneficiary
- }
- if transition.ValidatorSelectionMode != "" {
- newConfig.ValidatorSelectionMode = &transition.ValidatorSelectionMode
- }
- if transition.ValidatorContractAddress != (common.Address{}) {
- newConfig.ValidatorContract = transition.ValidatorContractAddress
- }
- if len(transition.Validators) > 0 {
- newConfig.Validators = transition.Validators
- }
- })
- return newConfig
- }
- func (c Config) GetValidatorContractAddress(blockNumber *big.Int) common.Address {
- validatorContractAddress := c.ValidatorContract
- c.getTransitionValue(blockNumber, func(transition params.Transition) {
- if (transition.ValidatorContractAddress != common.Address{}) {
- validatorContractAddress = transition.ValidatorContractAddress
- }
- })
- return validatorContractAddress
- }
- func (c Config) GetValidatorSelectionMode(blockNumber *big.Int) string {
- mode := params.BlockHeaderMode
- if c.ValidatorSelectionMode != nil {
- mode = *c.ValidatorSelectionMode
- }
- c.getTransitionValue(blockNumber, func(transition params.Transition) {
- if transition.ValidatorSelectionMode != "" {
- mode = transition.ValidatorSelectionMode
- }
- })
- return mode
- }
- func (c Config) GetValidatorsAt(blockNumber *big.Int) []common.Address {
- if blockNumber.Cmp(big.NewInt(0)) == 0 && len(c.Validators) > 0 {
- return c.Validators
- }
- if blockNumber != nil && c.Transitions != nil {
- for i := 0; i < len(c.Transitions) && c.Transitions[i].Block.Cmp(blockNumber) == 0; i++ {
- return c.Transitions[i].Validators
- }
- }
- //Note! empty means we will get the valset from previous block header which contains votes, validators etc
- return []common.Address{}
- }
- func (c Config) Get2FPlus1Enabled(blockNumber *big.Int) bool {
- twoFPlusOneEnabled := false
- c.getTransitionValue(blockNumber, func(transition params.Transition) {
- if transition.TwoFPlusOneEnabled != nil {
- twoFPlusOneEnabled = *transition.TwoFPlusOneEnabled
- }
- })
- return twoFPlusOneEnabled
- }
- func (c *Config) getTransitionValue(num *big.Int, callback func(transition params.Transition)) {
- if c != nil && num != nil && c.Transitions != nil {
- for i := 0; i < len(c.Transitions) && c.Transitions[i].Block.Cmp(num) <= 0; i++ {
- callback(c.Transitions[i])
- }
- }
- }
|