github.com/Oyster-zx/tendermint@v0.34.24-fork/statesync/snapshots.go (about) 1 package statesync 2 3 import ( 4 "crypto/sha256" 5 "fmt" 6 "math/rand" 7 "sort" 8 9 tmsync "github.com/tendermint/tendermint/libs/sync" 10 "github.com/tendermint/tendermint/p2p" 11 ) 12 13 // snapshotKey is a snapshot key used for lookups. 14 type snapshotKey [sha256.Size]byte 15 16 // snapshot contains data about a snapshot. 17 type snapshot struct { 18 Height uint64 19 Format uint32 20 Chunks uint32 21 Hash []byte 22 Metadata []byte 23 24 trustedAppHash []byte // populated by light client 25 } 26 27 // Key generates a snapshot key, used for lookups. It takes into account not only the height and 28 // format, but also the chunks, hash, and metadata in case peers have generated snapshots in a 29 // non-deterministic manner. All fields must be equal for the snapshot to be considered the same. 30 func (s *snapshot) Key() snapshotKey { 31 // Hash.Write() never returns an error. 32 hasher := sha256.New() 33 hasher.Write([]byte(fmt.Sprintf("%v:%v:%v", s.Height, s.Format, s.Chunks))) 34 hasher.Write(s.Hash) 35 hasher.Write(s.Metadata) 36 var key snapshotKey 37 copy(key[:], hasher.Sum(nil)) 38 return key 39 } 40 41 // snapshotPool discovers and aggregates snapshots across peers. 42 type snapshotPool struct { 43 tmsync.Mutex 44 snapshots map[snapshotKey]*snapshot 45 snapshotPeers map[snapshotKey]map[p2p.ID]p2p.Peer 46 47 // indexes for fast searches 48 formatIndex map[uint32]map[snapshotKey]bool 49 heightIndex map[uint64]map[snapshotKey]bool 50 peerIndex map[p2p.ID]map[snapshotKey]bool 51 52 // blacklists for rejected items 53 formatBlacklist map[uint32]bool 54 peerBlacklist map[p2p.ID]bool 55 snapshotBlacklist map[snapshotKey]bool 56 } 57 58 // newSnapshotPool creates a new snapshot pool. The state source is used for 59 func newSnapshotPool() *snapshotPool { 60 return &snapshotPool{ 61 snapshots: make(map[snapshotKey]*snapshot), 62 snapshotPeers: make(map[snapshotKey]map[p2p.ID]p2p.Peer), 63 formatIndex: make(map[uint32]map[snapshotKey]bool), 64 heightIndex: make(map[uint64]map[snapshotKey]bool), 65 peerIndex: make(map[p2p.ID]map[snapshotKey]bool), 66 formatBlacklist: make(map[uint32]bool), 67 peerBlacklist: make(map[p2p.ID]bool), 68 snapshotBlacklist: make(map[snapshotKey]bool), 69 } 70 } 71 72 // Add adds a snapshot to the pool, unless the peer has already sent recentSnapshots snapshots. It 73 // returns true if this was a new, non-blacklisted snapshot. The snapshot height is verified using 74 // the light client, and the expected app hash is set for the snapshot. 75 func (p *snapshotPool) Add(peer p2p.Peer, snapshot *snapshot) (bool, error) { 76 key := snapshot.Key() 77 78 p.Lock() 79 defer p.Unlock() 80 81 switch { 82 case p.formatBlacklist[snapshot.Format]: 83 return false, nil 84 case p.peerBlacklist[peer.ID()]: 85 return false, nil 86 case p.snapshotBlacklist[key]: 87 return false, nil 88 case len(p.peerIndex[peer.ID()]) >= recentSnapshots: 89 return false, nil 90 } 91 92 if p.snapshotPeers[key] == nil { 93 p.snapshotPeers[key] = make(map[p2p.ID]p2p.Peer) 94 } 95 p.snapshotPeers[key][peer.ID()] = peer 96 97 if p.peerIndex[peer.ID()] == nil { 98 p.peerIndex[peer.ID()] = make(map[snapshotKey]bool) 99 } 100 p.peerIndex[peer.ID()][key] = true 101 102 if p.snapshots[key] != nil { 103 return false, nil 104 } 105 p.snapshots[key] = snapshot 106 107 if p.formatIndex[snapshot.Format] == nil { 108 p.formatIndex[snapshot.Format] = make(map[snapshotKey]bool) 109 } 110 p.formatIndex[snapshot.Format][key] = true 111 112 if p.heightIndex[snapshot.Height] == nil { 113 p.heightIndex[snapshot.Height] = make(map[snapshotKey]bool) 114 } 115 p.heightIndex[snapshot.Height][key] = true 116 117 return true, nil 118 } 119 120 // Best returns the "best" currently known snapshot, if any. 121 func (p *snapshotPool) Best() *snapshot { 122 ranked := p.Ranked() 123 if len(ranked) == 0 { 124 return nil 125 } 126 return ranked[0] 127 } 128 129 // GetPeer returns a random peer for a snapshot, if any. 130 func (p *snapshotPool) GetPeer(snapshot *snapshot) p2p.Peer { 131 peers := p.GetPeers(snapshot) 132 if len(peers) == 0 { 133 return nil 134 } 135 return peers[rand.Intn(len(peers))] //nolint:gosec // G404: Use of weak random number generator 136 } 137 138 // GetPeers returns the peers for a snapshot. 139 func (p *snapshotPool) GetPeers(snapshot *snapshot) []p2p.Peer { 140 key := snapshot.Key() 141 p.Lock() 142 defer p.Unlock() 143 144 peers := make([]p2p.Peer, 0, len(p.snapshotPeers[key])) 145 for _, peer := range p.snapshotPeers[key] { 146 peers = append(peers, peer) 147 } 148 // sort results, for testability (otherwise order is random, so tests randomly fail) 149 sort.Slice(peers, func(a int, b int) bool { 150 return peers[a].ID() < peers[b].ID() 151 }) 152 return peers 153 } 154 155 // Ranked returns a list of snapshots ranked by preference. The current heuristic is very naïve, 156 // preferring the snapshot with the greatest height, then greatest format, then greatest number of 157 // peers. This can be improved quite a lot. 158 func (p *snapshotPool) Ranked() []*snapshot { 159 p.Lock() 160 defer p.Unlock() 161 162 candidates := make([]*snapshot, 0, len(p.snapshots)) 163 for key := range p.snapshots { 164 candidates = append(candidates, p.snapshots[key]) 165 } 166 167 sort.Slice(candidates, func(i, j int) bool { 168 a := candidates[i] 169 b := candidates[j] 170 171 switch { 172 case a.Height > b.Height: 173 return true 174 case a.Height < b.Height: 175 return false 176 case a.Format > b.Format: 177 return true 178 case a.Format < b.Format: 179 return false 180 case len(p.snapshotPeers[a.Key()]) > len(p.snapshotPeers[b.Key()]): 181 return true 182 default: 183 return false 184 } 185 }) 186 187 return candidates 188 } 189 190 // Reject rejects a snapshot. Rejected snapshots will never be used again. 191 func (p *snapshotPool) Reject(snapshot *snapshot) { 192 key := snapshot.Key() 193 p.Lock() 194 defer p.Unlock() 195 196 p.snapshotBlacklist[key] = true 197 p.removeSnapshot(key) 198 } 199 200 // RejectFormat rejects a snapshot format. It will never be used again. 201 func (p *snapshotPool) RejectFormat(format uint32) { 202 p.Lock() 203 defer p.Unlock() 204 205 p.formatBlacklist[format] = true 206 for key := range p.formatIndex[format] { 207 p.removeSnapshot(key) 208 } 209 } 210 211 // RejectPeer rejects a peer. It will never be used again. 212 func (p *snapshotPool) RejectPeer(peerID p2p.ID) { 213 if peerID == "" { 214 return 215 } 216 p.Lock() 217 defer p.Unlock() 218 219 p.removePeer(peerID) 220 p.peerBlacklist[peerID] = true 221 } 222 223 // RemovePeer removes a peer from the pool, and any snapshots that no longer have peers. 224 func (p *snapshotPool) RemovePeer(peerID p2p.ID) { 225 p.Lock() 226 defer p.Unlock() 227 p.removePeer(peerID) 228 } 229 230 // removePeer removes a peer. The caller must hold the mutex lock. 231 func (p *snapshotPool) removePeer(peerID p2p.ID) { 232 for key := range p.peerIndex[peerID] { 233 delete(p.snapshotPeers[key], peerID) 234 if len(p.snapshotPeers[key]) == 0 { 235 p.removeSnapshot(key) 236 } 237 } 238 delete(p.peerIndex, peerID) 239 } 240 241 // removeSnapshot removes a snapshot. The caller must hold the mutex lock. 242 func (p *snapshotPool) removeSnapshot(key snapshotKey) { 243 snapshot := p.snapshots[key] 244 if snapshot == nil { 245 return 246 } 247 248 delete(p.snapshots, key) 249 delete(p.formatIndex[snapshot.Format], key) 250 delete(p.heightIndex[snapshot.Height], key) 251 for peerID := range p.snapshotPeers[key] { 252 delete(p.peerIndex[peerID], key) 253 } 254 delete(p.snapshotPeers, key) 255 }