github.com/ethereum/go-ethereum@v1.14.3/consensus/clique/snapshot.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package clique 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "maps" 23 "slices" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/common/lru" 28 "github.com/ethereum/go-ethereum/core/rawdb" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/ethdb" 31 "github.com/ethereum/go-ethereum/log" 32 "github.com/ethereum/go-ethereum/params" 33 ) 34 35 // Vote represents a single vote that an authorized signer made to modify the 36 // list of authorizations. 37 type Vote struct { 38 Signer common.Address `json:"signer"` // Authorized signer that cast this vote 39 Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes) 40 Address common.Address `json:"address"` // Account being voted on to change its authorization 41 Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account 42 } 43 44 // Tally is a simple vote tally to keep the current score of votes. Votes that 45 // go against the proposal aren't counted since it's equivalent to not voting. 46 type Tally struct { 47 Authorize bool `json:"authorize"` // Whether the vote is about authorizing or kicking someone 48 Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal 49 } 50 51 type sigLRU = lru.Cache[common.Hash, common.Address] 52 53 // Snapshot is the state of the authorization voting at a given point in time. 54 type Snapshot struct { 55 config *params.CliqueConfig // Consensus engine parameters to fine tune behavior 56 sigcache *sigLRU // Cache of recent block signatures to speed up ecrecover 57 58 Number uint64 `json:"number"` // Block number where the snapshot was created 59 Hash common.Hash `json:"hash"` // Block hash where the snapshot was created 60 Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment 61 Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections 62 Votes []*Vote `json:"votes"` // List of votes cast in chronological order 63 Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating 64 } 65 66 // newSnapshot creates a new snapshot with the specified startup parameters. This 67 // method does not initialize the set of recent signers, so only ever use if for 68 // the genesis block. 69 func newSnapshot(config *params.CliqueConfig, sigcache *sigLRU, number uint64, hash common.Hash, signers []common.Address) *Snapshot { 70 snap := &Snapshot{ 71 config: config, 72 sigcache: sigcache, 73 Number: number, 74 Hash: hash, 75 Signers: make(map[common.Address]struct{}), 76 Recents: make(map[uint64]common.Address), 77 Tally: make(map[common.Address]Tally), 78 } 79 for _, signer := range signers { 80 snap.Signers[signer] = struct{}{} 81 } 82 return snap 83 } 84 85 // loadSnapshot loads an existing snapshot from the database. 86 func loadSnapshot(config *params.CliqueConfig, sigcache *sigLRU, db ethdb.Database, hash common.Hash) (*Snapshot, error) { 87 blob, err := db.Get(append(rawdb.CliqueSnapshotPrefix, hash[:]...)) 88 if err != nil { 89 return nil, err 90 } 91 snap := new(Snapshot) 92 if err := json.Unmarshal(blob, snap); err != nil { 93 return nil, err 94 } 95 snap.config = config 96 snap.sigcache = sigcache 97 98 return snap, nil 99 } 100 101 // store inserts the snapshot into the database. 102 func (s *Snapshot) store(db ethdb.Database) error { 103 blob, err := json.Marshal(s) 104 if err != nil { 105 return err 106 } 107 return db.Put(append(rawdb.CliqueSnapshotPrefix, s.Hash[:]...), blob) 108 } 109 110 // copy creates a deep copy of the snapshot, though not the individual votes. 111 func (s *Snapshot) copy() *Snapshot { 112 return &Snapshot{ 113 config: s.config, 114 sigcache: s.sigcache, 115 Number: s.Number, 116 Hash: s.Hash, 117 Signers: maps.Clone(s.Signers), 118 Recents: maps.Clone(s.Recents), 119 Votes: slices.Clone(s.Votes), 120 Tally: maps.Clone(s.Tally), 121 } 122 } 123 124 // validVote returns whether it makes sense to cast the specified vote in the 125 // given snapshot context (e.g. don't try to add an already authorized signer). 126 func (s *Snapshot) validVote(address common.Address, authorize bool) bool { 127 _, signer := s.Signers[address] 128 return (signer && !authorize) || (!signer && authorize) 129 } 130 131 // cast adds a new vote into the tally. 132 func (s *Snapshot) cast(address common.Address, authorize bool) bool { 133 // Ensure the vote is meaningful 134 if !s.validVote(address, authorize) { 135 return false 136 } 137 // Cast the vote into an existing or new tally 138 if old, ok := s.Tally[address]; ok { 139 old.Votes++ 140 s.Tally[address] = old 141 } else { 142 s.Tally[address] = Tally{Authorize: authorize, Votes: 1} 143 } 144 return true 145 } 146 147 // uncast removes a previously cast vote from the tally. 148 func (s *Snapshot) uncast(address common.Address, authorize bool) bool { 149 // If there's no tally, it's a dangling vote, just drop 150 tally, ok := s.Tally[address] 151 if !ok { 152 return false 153 } 154 // Ensure we only revert counted votes 155 if tally.Authorize != authorize { 156 return false 157 } 158 // Otherwise revert the vote 159 if tally.Votes > 1 { 160 tally.Votes-- 161 s.Tally[address] = tally 162 } else { 163 delete(s.Tally, address) 164 } 165 return true 166 } 167 168 // apply creates a new authorization snapshot by applying the given headers to 169 // the original one. 170 func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { 171 // Allow passing in no headers for cleaner code 172 if len(headers) == 0 { 173 return s, nil 174 } 175 // Sanity check that the headers can be applied 176 for i := 0; i < len(headers)-1; i++ { 177 if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { 178 return nil, errInvalidVotingChain 179 } 180 } 181 if headers[0].Number.Uint64() != s.Number+1 { 182 return nil, errInvalidVotingChain 183 } 184 // Iterate through the headers and create a new snapshot 185 snap := s.copy() 186 187 var ( 188 start = time.Now() 189 logged = time.Now() 190 ) 191 for i, header := range headers { 192 // Remove any votes on checkpoint blocks 193 number := header.Number.Uint64() 194 if number%s.config.Epoch == 0 { 195 snap.Votes = nil 196 snap.Tally = make(map[common.Address]Tally) 197 } 198 // Delete the oldest signer from the recent list to allow it signing again 199 if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { 200 delete(snap.Recents, number-limit) 201 } 202 // Resolve the authorization key and check against signers 203 signer, err := ecrecover(header, s.sigcache) 204 if err != nil { 205 return nil, err 206 } 207 if _, ok := snap.Signers[signer]; !ok { 208 return nil, errUnauthorizedSigner 209 } 210 for _, recent := range snap.Recents { 211 if recent == signer { 212 return nil, errRecentlySigned 213 } 214 } 215 snap.Recents[number] = signer 216 217 // Header authorized, discard any previous votes from the signer 218 for i, vote := range snap.Votes { 219 if vote.Signer == signer && vote.Address == header.Coinbase { 220 // Uncast the vote from the cached tally 221 snap.uncast(vote.Address, vote.Authorize) 222 223 // Uncast the vote from the chronological list 224 snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) 225 break // only one vote allowed 226 } 227 } 228 // Tally up the new vote from the signer 229 var authorize bool 230 switch { 231 case bytes.Equal(header.Nonce[:], nonceAuthVote): 232 authorize = true 233 case bytes.Equal(header.Nonce[:], nonceDropVote): 234 authorize = false 235 default: 236 return nil, errInvalidVote 237 } 238 if snap.cast(header.Coinbase, authorize) { 239 snap.Votes = append(snap.Votes, &Vote{ 240 Signer: signer, 241 Block: number, 242 Address: header.Coinbase, 243 Authorize: authorize, 244 }) 245 } 246 // If the vote passed, update the list of signers 247 if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 { 248 if tally.Authorize { 249 snap.Signers[header.Coinbase] = struct{}{} 250 } else { 251 delete(snap.Signers, header.Coinbase) 252 253 // Signer list shrunk, delete any leftover recent caches 254 if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { 255 delete(snap.Recents, number-limit) 256 } 257 // Discard any previous votes the deauthorized signer cast 258 for i := 0; i < len(snap.Votes); i++ { 259 if snap.Votes[i].Signer == header.Coinbase { 260 // Uncast the vote from the cached tally 261 snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize) 262 263 // Uncast the vote from the chronological list 264 snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) 265 266 i-- 267 } 268 } 269 } 270 // Discard any previous votes around the just changed account 271 for i := 0; i < len(snap.Votes); i++ { 272 if snap.Votes[i].Address == header.Coinbase { 273 snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) 274 i-- 275 } 276 } 277 delete(snap.Tally, header.Coinbase) 278 } 279 // If we're taking too much time (ecrecover), notify the user once a while 280 if time.Since(logged) > 8*time.Second { 281 log.Info("Reconstructing voting history", "processed", i, "total", len(headers), "elapsed", common.PrettyDuration(time.Since(start))) 282 logged = time.Now() 283 } 284 } 285 if time.Since(start) > 8*time.Second { 286 log.Info("Reconstructed voting history", "processed", len(headers), "elapsed", common.PrettyDuration(time.Since(start))) 287 } 288 snap.Number += uint64(len(headers)) 289 snap.Hash = headers[len(headers)-1].Hash() 290 291 return snap, nil 292 } 293 294 // signers retrieves the list of authorized signers in ascending order. 295 func (s *Snapshot) signers() []common.Address { 296 sigs := make([]common.Address, 0, len(s.Signers)) 297 for sig := range s.Signers { 298 sigs = append(sigs, sig) 299 } 300 slices.SortFunc(sigs, common.Address.Cmp) 301 return sigs 302 } 303 304 // inturn returns if a signer at a given block height is in-turn or not. 305 func (s *Snapshot) inturn(number uint64, signer common.Address) bool { 306 signers, offset := s.signers(), 0 307 for offset < len(signers) && signers[offset] != signer { 308 offset++ 309 } 310 return (number % uint64(len(signers))) == uint64(offset) 311 }