github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/backend/snapshot.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2017 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from quorum/consensus/istanbul/backend/snapshot.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package backend
    22  
    23  import (
    24  	"bytes"
    25  	"encoding/json"
    26  	"math/big"
    27  
    28  	"github.com/klaytn/klaytn/consensus"
    29  
    30  	"github.com/klaytn/klaytn/blockchain/types"
    31  	"github.com/klaytn/klaytn/common"
    32  	"github.com/klaytn/klaytn/consensus/istanbul"
    33  	"github.com/klaytn/klaytn/consensus/istanbul/validator"
    34  	"github.com/klaytn/klaytn/governance"
    35  	"github.com/klaytn/klaytn/params"
    36  	"github.com/klaytn/klaytn/storage/database"
    37  )
    38  
    39  const (
    40  	dbKeySnapshotPrefix = "istanbul-snapshot"
    41  )
    42  
    43  // Snapshot is the state of the authorization voting at a given point in time.
    44  type Snapshot struct {
    45  	Epoch         uint64                // The number of blocks after which to checkpoint and reset the pending votes
    46  	Number        uint64                // Block number where the snapshot was created
    47  	Hash          common.Hash           // Block hash where the snapshot was created
    48  	ValSet        istanbul.ValidatorSet // Set of authorized validators at this moment
    49  	Policy        uint64
    50  	CommitteeSize uint64
    51  	Votes         []governance.GovernanceVote      // List of votes cast in chronological order
    52  	Tally         []governance.GovernanceTallyItem // Current vote tally to avoid recalculating
    53  }
    54  
    55  func effectiveParams(gov governance.Engine, number uint64) (epoch uint64, policy uint64, committeeSize uint64) {
    56  	pset, err := gov.EffectiveParams(number)
    57  	if err != nil {
    58  		// TODO-Klaytn-Kore: remove err condition
    59  		logger.Error("Couldn't get governance value. Resorting to defaults", "err", err)
    60  		epoch = params.DefaultEpoch
    61  		policy = params.DefaultProposerPolicy
    62  		committeeSize = params.DefaultSubGroupSize
    63  	} else {
    64  		epoch = pset.Epoch()
    65  		policy = pset.Policy()
    66  		committeeSize = pset.CommitteeSize()
    67  	}
    68  
    69  	return
    70  }
    71  
    72  // newSnapshot create a new snapshot with the specified startup parameters. This
    73  // method does not initialize the set of recent validators, so only ever use if for
    74  // the genesis block.
    75  func newSnapshot(gov governance.Engine, number uint64, hash common.Hash, valSet istanbul.ValidatorSet, chainConfig *params.ChainConfig) *Snapshot {
    76  	epoch, policy, committeeSize := effectiveParams(gov, number+1)
    77  
    78  	snap := &Snapshot{
    79  		Epoch:         epoch,
    80  		Number:        number,
    81  		Hash:          hash,
    82  		ValSet:        valSet,
    83  		Policy:        policy,
    84  		CommitteeSize: committeeSize,
    85  		Votes:         make([]governance.GovernanceVote, 0),
    86  		Tally:         make([]governance.GovernanceTallyItem, 0),
    87  	}
    88  	return snap
    89  }
    90  
    91  // loadSnapshot loads an existing snapshot from the database.
    92  func loadSnapshot(db database.DBManager, hash common.Hash) (*Snapshot, error) {
    93  	blob, err := db.ReadIstanbulSnapshot(hash)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	snap := new(Snapshot)
    98  	if err := json.Unmarshal(blob, snap); err != nil {
    99  		return nil, err
   100  	}
   101  	return snap, nil
   102  }
   103  
   104  // store inserts the snapshot into the database.
   105  func (s *Snapshot) store(db database.DBManager) error {
   106  	blob, err := json.Marshal(s)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	return db.WriteIstanbulSnapshot(s.Hash, blob)
   112  }
   113  
   114  // copy creates a deep copy of the snapshot, though not the individual votes.
   115  func (s *Snapshot) copy() *Snapshot {
   116  	cpy := &Snapshot{
   117  		Epoch:         s.Epoch,
   118  		Number:        s.Number,
   119  		Hash:          s.Hash,
   120  		ValSet:        s.ValSet.Copy(),
   121  		Policy:        s.Policy,
   122  		CommitteeSize: s.CommitteeSize,
   123  		Votes:         make([]governance.GovernanceVote, len(s.Votes)),
   124  		Tally:         make([]governance.GovernanceTallyItem, len(s.Tally)),
   125  	}
   126  
   127  	copy(cpy.Votes, s.Votes)
   128  	copy(cpy.Tally, s.Tally)
   129  
   130  	return cpy
   131  }
   132  
   133  // checkVote return whether it's a valid vote
   134  func (s *Snapshot) checkVote(address common.Address, authorize bool) bool {
   135  	_, validator := s.ValSet.GetByAddress(address)
   136  	return (validator != nil && !authorize) || (validator == nil && authorize)
   137  }
   138  
   139  // apply creates a new authorization snapshot by applying the given headers to
   140  // the original one.
   141  func (s *Snapshot) apply(headers []*types.Header, gov governance.Engine, addr common.Address, policy uint64, chain consensus.ChainReader, writable bool) (*Snapshot, error) {
   142  	// Allow passing in no headers for cleaner code
   143  	if len(headers) == 0 {
   144  		return s, nil
   145  	}
   146  	// Sanity check that the headers can be applied
   147  	for i := 0; i < len(headers)-1; i++ {
   148  		if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
   149  			return nil, errInvalidVotingChain
   150  		}
   151  	}
   152  	if headers[0].Number.Uint64() != s.Number+1 {
   153  		return nil, errInvalidVotingChain
   154  	}
   155  
   156  	// Iterate through the headers and create a new snapshot
   157  	snap := s.copy()
   158  
   159  	// Copy values which might be changed by governance vote
   160  	snap.Epoch, snap.Policy, snap.CommitteeSize = effectiveParams(gov, snap.Number+1)
   161  
   162  	for _, header := range headers {
   163  		// Remove any votes on checkpoint blocks
   164  		number := header.Number.Uint64()
   165  
   166  		// Resolve the authorization key and check against validators
   167  		validator, err := ecrecover(header)
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  		if _, v := snap.ValSet.GetByAddress(validator); v == nil {
   172  			return nil, errUnauthorized
   173  		}
   174  
   175  		if number%snap.Epoch == 0 {
   176  			if writable {
   177  				gov.UpdateCurrentSet(number)
   178  				if len(header.Governance) > 0 {
   179  					gov.WriteGovernanceForNextEpoch(number, header.Governance)
   180  				}
   181  				gov.ClearVotes(number)
   182  			}
   183  			snap.Votes = make([]governance.GovernanceVote, 0)
   184  			snap.Tally = make([]governance.GovernanceTallyItem, 0)
   185  		}
   186  
   187  		// Reload governance values
   188  		snap.Epoch, snap.Policy, snap.CommitteeSize = effectiveParams(gov, number+1)
   189  
   190  		snap.ValSet, snap.Votes, snap.Tally = gov.HandleGovernanceVote(snap.ValSet, snap.Votes, snap.Tally, header, validator, addr, writable)
   191  		if policy == uint64(params.WeightedRandom) {
   192  			// Snapshot of block N (Snapshot_N) should contain proposers for N+1 and following blocks.
   193  			// Validators for Block N+1 can be calculated based on the staking information from the previous stakingUpdateInterval block.
   194  			// If the governance mode is single, the governing node is added to validator all the time.
   195  			//
   196  			// Proposers for Block N+1 can be calculated from the nearest previous proposersUpdateInterval block.
   197  			// Refresh proposers in Snapshot_N using previous proposersUpdateInterval block for N+1, if not updated yet.
   198  
   199  			// because snapshot(num)'s ValSet = validators for num+1
   200  			pset, err := gov.EffectiveParams(number + 1)
   201  			if err != nil {
   202  				return nil, err
   203  			}
   204  
   205  			isSingle := (pset.GovernanceModeInt() == params.GovernanceMode_Single)
   206  			govNode := pset.GoverningNode()
   207  			minStaking := pset.MinimumStakeBig().Uint64()
   208  
   209  			pHeader := chain.GetHeaderByNumber(params.CalcProposerBlockNumber(number + 1))
   210  			if pHeader != nil {
   211  				if err := snap.ValSet.Refresh(pHeader.Hash(), pHeader.Number.Uint64(), chain.Config(), isSingle, govNode, minStaking); err != nil {
   212  					// There are three error cases and they just don't refresh proposers
   213  					// (1) no validator at all
   214  					// (2) invalid formatted hash
   215  					// (3) no staking info available
   216  					logger.Trace("Skip refreshing proposers while creating snapshot", "snap.Number", snap.Number, "pHeader.Number", pHeader.Number.Uint64(), "err", err)
   217  				}
   218  			} else {
   219  				logger.Trace("Can't refreshing proposers while creating snapshot due to lack of required header", "snap.Number", snap.Number)
   220  			}
   221  		}
   222  	}
   223  	snap.Number += uint64(len(headers))
   224  	snap.Hash = headers[len(headers)-1].Hash()
   225  
   226  	if snap.ValSet.Policy() == istanbul.WeightedRandom {
   227  		snap.ValSet.SetBlockNum(snap.Number)
   228  
   229  		bigNum := new(big.Int).SetUint64(snap.Number)
   230  		if chain.Config().IsRandaoForkBlockParent(bigNum) {
   231  			// The ForkBlock must select proposers using MixHash but (ForkBlock - 1) has no MixHash. Using ZeroMixHash instead.
   232  			snap.ValSet.SetMixHash(params.ZeroMixHash)
   233  		} else if chain.Config().IsRandaoForkEnabled(bigNum) {
   234  			// Feed parent MixHash
   235  			snap.ValSet.SetMixHash(headers[len(headers)-1].MixHash)
   236  		}
   237  	}
   238  	snap.ValSet.SetSubGroupSize(snap.CommitteeSize)
   239  
   240  	if writable {
   241  		gov.SetTotalVotingPower(snap.ValSet.TotalVotingPower())
   242  		gov.SetMyVotingPower(snap.getMyVotingPower(addr))
   243  	}
   244  
   245  	return snap, nil
   246  }
   247  
   248  func (s *Snapshot) getMyVotingPower(addr common.Address) uint64 {
   249  	for _, a := range s.ValSet.List() {
   250  		if a.Address() == addr {
   251  			return a.VotingPower()
   252  		}
   253  	}
   254  	return 0
   255  }
   256  
   257  // validators retrieves the list of authorized validators in ascending order.
   258  func (s *Snapshot) validators() []common.Address {
   259  	validators := make([]common.Address, 0, s.ValSet.Size())
   260  	for _, validator := range s.ValSet.List() {
   261  		validators = append(validators, validator.Address())
   262  	}
   263  	return sortValidatorArray(validators)
   264  }
   265  
   266  // demotedValidators retrieves the list of authorized, but demoted validators in ascending order.
   267  func (s *Snapshot) demotedValidators() []common.Address {
   268  	demotedValidators := make([]common.Address, 0, len(s.ValSet.DemotedList()))
   269  	for _, demotedValidator := range s.ValSet.DemotedList() {
   270  		demotedValidators = append(demotedValidators, demotedValidator.Address())
   271  	}
   272  	return sortValidatorArray(demotedValidators)
   273  }
   274  
   275  func (s *Snapshot) committee(prevHash common.Hash, view *istanbul.View) []common.Address {
   276  	committeeList := s.ValSet.SubList(prevHash, view)
   277  
   278  	committee := make([]common.Address, 0, len(committeeList))
   279  	for _, v := range committeeList {
   280  		committee = append(committee, v.Address())
   281  	}
   282  	return committee
   283  }
   284  
   285  func sortValidatorArray(validators []common.Address) []common.Address {
   286  	for i := 0; i < len(validators); i++ {
   287  		for j := i + 1; j < len(validators); j++ {
   288  			if bytes.Compare(validators[i][:], validators[j][:]) > 0 {
   289  				validators[i], validators[j] = validators[j], validators[i]
   290  			}
   291  		}
   292  	}
   293  	return validators
   294  }
   295  
   296  type snapshotJSON struct {
   297  	Epoch  uint64                           `json:"epoch"`
   298  	Number uint64                           `json:"number"`
   299  	Hash   common.Hash                      `json:"hash"`
   300  	Votes  []governance.GovernanceVote      `json:"votes"`
   301  	Tally  []governance.GovernanceTallyItem `json:"tally"`
   302  
   303  	// for validator set
   304  	Validators   []common.Address        `json:"validators"`
   305  	Policy       istanbul.ProposerPolicy `json:"policy"`
   306  	SubGroupSize uint64                  `json:"subgroupsize"`
   307  
   308  	// for weighted validator
   309  	RewardAddrs       []common.Address `json:"rewardAddrs"`
   310  	VotingPowers      []uint64         `json:"votingPower"`
   311  	Weights           []uint64         `json:"weight"`
   312  	Proposers         []common.Address `json:"proposers"`
   313  	ProposersBlockNum uint64           `json:"proposersBlockNum"`
   314  	DemotedValidators []common.Address `json:"demotedValidators"`
   315  	MixHash           []byte           `json:"mixHash,omitempty"`
   316  }
   317  
   318  func (s *Snapshot) toJSONStruct() *snapshotJSON {
   319  	var rewardAddrs []common.Address
   320  	var votingPowers []uint64
   321  	var weights []uint64
   322  	var proposers []common.Address
   323  	var proposersBlockNum uint64
   324  	var validators []common.Address
   325  	var demotedValidators []common.Address
   326  	var mixHash []byte
   327  
   328  	if s.ValSet.Policy() == istanbul.WeightedRandom {
   329  		validators, demotedValidators, rewardAddrs, votingPowers, weights, proposers, proposersBlockNum, mixHash = validator.GetWeightedCouncilData(s.ValSet)
   330  	} else {
   331  		validators = s.validators()
   332  	}
   333  
   334  	return &snapshotJSON{
   335  		Epoch:             s.Epoch,
   336  		Number:            s.Number,
   337  		Hash:              s.Hash,
   338  		Votes:             s.Votes,
   339  		Tally:             s.Tally,
   340  		Validators:        validators,
   341  		Policy:            istanbul.ProposerPolicy(s.Policy),
   342  		SubGroupSize:      s.CommitteeSize,
   343  		RewardAddrs:       rewardAddrs,
   344  		VotingPowers:      votingPowers,
   345  		Weights:           weights,
   346  		Proposers:         proposers,
   347  		ProposersBlockNum: proposersBlockNum,
   348  		DemotedValidators: demotedValidators,
   349  		MixHash:           mixHash,
   350  	}
   351  }
   352  
   353  // Unmarshal from a json byte array
   354  func (s *Snapshot) UnmarshalJSON(b []byte) error {
   355  	var j snapshotJSON
   356  	if err := json.Unmarshal(b, &j); err != nil {
   357  		return err
   358  	}
   359  
   360  	s.Epoch = j.Epoch
   361  	s.Number = j.Number
   362  	s.Hash = j.Hash
   363  	s.Votes = j.Votes
   364  	s.Tally = j.Tally
   365  
   366  	if j.Policy == istanbul.WeightedRandom {
   367  		s.ValSet = validator.NewWeightedCouncil(j.Validators, j.DemotedValidators, j.RewardAddrs, j.VotingPowers, j.Weights, j.Policy, j.SubGroupSize, j.Number, j.ProposersBlockNum, nil)
   368  		validator.RecoverWeightedCouncilProposer(s.ValSet, j.Proposers)
   369  		s.ValSet.SetMixHash(j.MixHash)
   370  	} else {
   371  		s.ValSet = validator.NewSubSet(j.Validators, j.Policy, j.SubGroupSize)
   372  	}
   373  	return nil
   374  }
   375  
   376  // Marshal to a json byte array
   377  func (s *Snapshot) MarshalJSON() ([]byte, error) {
   378  	j := s.toJSONStruct()
   379  	return json.Marshal(j)
   380  }