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 }