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 }