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