github.com/hyperion-hyn/go-ethereum@v2.4.0+incompatible/raft/snapshot.go (about) 1 package raft 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "math/big" 8 "net" 9 "sort" 10 "time" 11 12 "github.com/coreos/etcd/raft/raftpb" 13 "github.com/coreos/etcd/snap" 14 "github.com/coreos/etcd/wal/walpb" 15 mapset "github.com/deckarep/golang-set" 16 17 "github.com/ethereum/go-ethereum/common" 18 "github.com/ethereum/go-ethereum/core/types" 19 "github.com/ethereum/go-ethereum/eth/downloader" 20 "github.com/ethereum/go-ethereum/log" 21 "github.com/ethereum/go-ethereum/p2p/enode" 22 "github.com/ethereum/go-ethereum/p2p/enr" 23 "github.com/ethereum/go-ethereum/rlp" 24 ) 25 26 type SnapshotWithHostnames struct { 27 Addresses []Address 28 RemovedRaftIds []uint16 29 HeadBlockHash common.Hash 30 } 31 32 type AddressWithoutHostname struct { 33 RaftId uint16 34 NodeId enode.EnodeID 35 Ip net.IP 36 P2pPort enr.TCP 37 RaftPort enr.RaftPort 38 } 39 40 type SnapshotWithoutHostnames struct { 41 Addresses []AddressWithoutHostname 42 RemovedRaftIds []uint16 // Raft IDs for permanently removed peers 43 HeadBlockHash common.Hash 44 } 45 46 type ByRaftId []Address 47 48 func (a ByRaftId) Len() int { return len(a) } 49 func (a ByRaftId) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 50 func (a ByRaftId) Less(i, j int) bool { return a[i].RaftId < a[j].RaftId } 51 52 func (pm *ProtocolManager) buildSnapshot() *SnapshotWithHostnames { 53 pm.mu.RLock() 54 defer pm.mu.RUnlock() 55 56 numNodes := len(pm.confState.Nodes) + len(pm.confState.Learners) 57 numRemovedNodes := pm.removedPeers.Cardinality() 58 59 snapshot := &SnapshotWithHostnames{ 60 Addresses: make([]Address, numNodes), 61 RemovedRaftIds: make([]uint16, numRemovedNodes), 62 HeadBlockHash: pm.blockchain.CurrentBlock().Hash(), 63 } 64 65 // Populate addresses 66 67 for i, rawRaftId := range append(pm.confState.Nodes, pm.confState.Learners...) { 68 raftId := uint16(rawRaftId) 69 70 if raftId == pm.raftId { 71 snapshot.Addresses[i] = *pm.address 72 } else { 73 snapshot.Addresses[i] = *pm.peers[raftId].address 74 } 75 } 76 sort.Sort(ByRaftId(snapshot.Addresses)) 77 78 // Populate removed IDs 79 i := 0 80 for removedIface := range pm.removedPeers.Iterator().C { 81 snapshot.RemovedRaftIds[i] = removedIface.(uint16) 82 i++ 83 } 84 return snapshot 85 } 86 87 // Note that we do *not* read `pm.appliedIndex` here. We only use the `index` 88 // parameter instead. This is because we need to support a scenario when we 89 // snapshot for a future index that we have not yet recorded in LevelDB. See 90 // comments around the use of `forceSnapshot`. 91 func (pm *ProtocolManager) triggerSnapshot(index uint64) { 92 pm.mu.RLock() 93 snapshotIndex := pm.snapshotIndex 94 pm.mu.RUnlock() 95 96 log.Info("start snapshot", "applied index", pm.appliedIndex, "last snapshot index", snapshotIndex) 97 98 //snapData := pm.blockchain.CurrentBlock().Hash().Bytes() 99 //snap, err := pm.raftStorage.CreateSnapshot(pm.appliedIndex, &pm.confState, snapData) 100 snapData := pm.buildSnapshot().toBytes(pm.useDns) 101 snap, err := pm.raftStorage.CreateSnapshot(index, &pm.confState, snapData) 102 if err != nil { 103 panic(err) 104 } 105 if err := pm.saveRaftSnapshot(snap); err != nil { 106 panic(err) 107 } 108 // Discard all log entries prior to index. 109 if err := pm.raftStorage.Compact(index); err != nil { 110 panic(err) 111 } 112 log.Info("compacted log", "index", pm.appliedIndex) 113 114 pm.mu.Lock() 115 pm.snapshotIndex = index 116 pm.mu.Unlock() 117 } 118 119 func confStateIdSet(confState raftpb.ConfState) mapset.Set { 120 set := mapset.NewSet() 121 for _, rawRaftId := range append(confState.Nodes, confState.Learners...) { 122 set.Add(uint16(rawRaftId)) 123 } 124 return set 125 } 126 127 func (pm *ProtocolManager) updateClusterMembership(newConfState raftpb.ConfState, addresses []Address, removedRaftIds []uint16) { 128 log.Info("updating cluster membership per raft snapshot") 129 130 prevConfState := pm.confState 131 132 // Update tombstones for permanently removed peers. For simplicity we do not 133 // allow the re-use of peer IDs once a peer is removed. 134 135 removedPeers := mapset.NewSet() 136 for _, removedRaftId := range removedRaftIds { 137 removedPeers.Add(removedRaftId) 138 } 139 pm.mu.Lock() 140 pm.removedPeers = removedPeers 141 pm.mu.Unlock() 142 143 // Remove old peers that we're still connected to 144 145 prevIds := confStateIdSet(prevConfState) 146 newIds := confStateIdSet(newConfState) 147 idsToRemove := prevIds.Difference(newIds) 148 for idIfaceToRemove := range idsToRemove.Iterator().C { 149 raftId := idIfaceToRemove.(uint16) 150 log.Info("removing old raft peer", "peer id", raftId) 151 152 pm.removePeer(raftId) 153 } 154 155 // Update local and remote addresses 156 157 for _, tempAddress := range addresses { 158 address := tempAddress // Allocate separately on the heap for each iteration. 159 160 if address.RaftId == pm.raftId { 161 // If we're a newcomer to an existing cluster, this is where we learn 162 // our own Address. 163 pm.setLocalAddress(&address) 164 } else { 165 pm.mu.RLock() 166 existingPeer := pm.peers[address.RaftId] 167 pm.mu.RUnlock() 168 169 if existingPeer == nil { 170 log.Info("adding new raft peer", "raft id", address.RaftId) 171 pm.addPeer(&address) 172 } 173 } 174 } 175 176 pm.mu.Lock() 177 pm.confState = newConfState 178 pm.mu.Unlock() 179 180 log.Info("updated cluster membership") 181 } 182 183 func (pm *ProtocolManager) maybeTriggerSnapshot() { 184 pm.mu.RLock() 185 appliedIndex := pm.appliedIndex 186 entriesSinceLastSnap := appliedIndex - pm.snapshotIndex 187 pm.mu.RUnlock() 188 189 if entriesSinceLastSnap < snapshotPeriod { 190 return 191 } 192 193 pm.triggerSnapshot(appliedIndex) 194 } 195 196 func (pm *ProtocolManager) loadSnapshot() *raftpb.Snapshot { 197 if raftSnapshot := pm.readRaftSnapshot(); raftSnapshot != nil { 198 log.Info("loading snapshot") 199 pm.applyRaftSnapshot(*raftSnapshot) 200 201 return raftSnapshot 202 } else { 203 log.Info("no snapshot to load") 204 205 return nil 206 } 207 } 208 209 func (snapshot *SnapshotWithHostnames) toBytes(useDns bool) []byte { 210 // we have DNS enabled, so only use the new snapshot type 211 if useDns { 212 buffer, err := rlp.EncodeToBytes(snapshot) 213 if err != nil { 214 panic(fmt.Sprintf("error: failed to RLP-encode Snapshot: %s", err.Error())) 215 } 216 return buffer 217 } 218 219 // DNS is not enabled, use the old snapshot type, converting from hostnames to IP addresses 220 oldSnapshot := new(SnapshotWithoutHostnames) 221 oldSnapshot.HeadBlockHash, oldSnapshot.RemovedRaftIds = snapshot.HeadBlockHash, snapshot.RemovedRaftIds 222 oldSnapshot.Addresses = make([]AddressWithoutHostname, len(snapshot.Addresses)) 223 224 for index, addrWithHost := range snapshot.Addresses { 225 oldSnapshot.Addresses[index] = AddressWithoutHostname{ 226 addrWithHost.RaftId, 227 addrWithHost.NodeId, 228 net.ParseIP(addrWithHost.Hostname), 229 addrWithHost.P2pPort, 230 addrWithHost.RaftPort, 231 } 232 } 233 234 buffer, err := rlp.EncodeToBytes(oldSnapshot) 235 if err != nil { 236 panic(fmt.Sprintf("error: failed to RLP-encode Snapshot: %s", err.Error())) 237 } 238 return buffer 239 } 240 241 func bytesToSnapshot(input []byte) *SnapshotWithHostnames { 242 var err, errOld error 243 244 snapshot := new(SnapshotWithHostnames) 245 streamNewSnapshot := rlp.NewStream(bytes.NewReader(input), 0) 246 if err = streamNewSnapshot.Decode(snapshot); err == nil { 247 return snapshot 248 } 249 250 snapshotOld := new(SnapshotWithoutHostnames) 251 streamOldSnapshot := rlp.NewStream(bytes.NewReader(input), 0) 252 if errOld = streamOldSnapshot.Decode(snapshotOld); errOld == nil { 253 var snapshotConverted SnapshotWithHostnames 254 snapshotConverted.RemovedRaftIds, snapshotConverted.HeadBlockHash = snapshotOld.RemovedRaftIds, snapshotOld.HeadBlockHash 255 snapshotConverted.Addresses = make([]Address, len(snapshotOld.Addresses)) 256 257 for index, oldAddrWithIp := range snapshotOld.Addresses { 258 snapshotConverted.Addresses[index] = Address{ 259 RaftId: oldAddrWithIp.RaftId, 260 NodeId: oldAddrWithIp.NodeId, 261 Ip: nil, 262 P2pPort: oldAddrWithIp.P2pPort, 263 RaftPort: oldAddrWithIp.RaftPort, 264 Hostname: oldAddrWithIp.Ip.String(), 265 } 266 } 267 268 return &snapshotConverted 269 } 270 271 fatalf("failed to RLP-decode Snapshot: %v, %v", err, errOld) 272 return nil 273 } 274 275 func (snapshot *SnapshotWithHostnames) EncodeRLP(w io.Writer) error { 276 return rlp.Encode(w, []interface{}{snapshot.Addresses, snapshot.RemovedRaftIds, snapshot.HeadBlockHash}) 277 } 278 279 // Raft snapshot 280 281 func (pm *ProtocolManager) saveRaftSnapshot(snap raftpb.Snapshot) error { 282 if err := pm.snapshotter.SaveSnap(snap); err != nil { 283 return err 284 } 285 286 walSnap := walpb.Snapshot{ 287 Index: snap.Metadata.Index, 288 Term: snap.Metadata.Term, 289 } 290 291 if err := pm.wal.SaveSnapshot(walSnap); err != nil { 292 return err 293 } 294 295 return pm.wal.ReleaseLockTo(snap.Metadata.Index) 296 } 297 298 func (pm *ProtocolManager) readRaftSnapshot() *raftpb.Snapshot { 299 snapshot, err := pm.snapshotter.Load() 300 if err != nil && err != snap.ErrNoSnapshot { 301 fatalf("error loading snapshot: %v", err) 302 } 303 304 return snapshot 305 } 306 307 func (pm *ProtocolManager) applyRaftSnapshot(raftSnapshot raftpb.Snapshot) { 308 log.Info("applying snapshot to raft storage") 309 if err := pm.raftStorage.ApplySnapshot(raftSnapshot); err != nil { 310 fatalf("failed to apply snapshot: %s", err) 311 } 312 snapshot := bytesToSnapshot(raftSnapshot.Data) 313 314 latestBlockHash := snapshot.HeadBlockHash 315 316 pm.updateClusterMembership(raftSnapshot.Metadata.ConfState, snapshot.Addresses, snapshot.RemovedRaftIds) 317 318 preSyncHead := pm.blockchain.CurrentBlock() 319 320 if latestBlock := pm.blockchain.GetBlockByHash(latestBlockHash); latestBlock == nil { 321 pm.syncBlockchainUntil(latestBlockHash) 322 pm.logNewlyAcceptedTransactions(preSyncHead) 323 324 log.Info(chainExtensionMessage, "hash", pm.blockchain.CurrentBlock().Hash()) 325 } else { 326 // added for permissions changes to indicate node sync up has started 327 types.SetSyncStatus() 328 log.Info("blockchain is caught up; no need to synchronize") 329 } 330 331 snapMeta := raftSnapshot.Metadata 332 pm.confState = snapMeta.ConfState 333 pm.mu.Lock() 334 pm.snapshotIndex = snapMeta.Index 335 pm.mu.Unlock() 336 } 337 338 func (pm *ProtocolManager) syncBlockchainUntil(hash common.Hash) { 339 pm.mu.RLock() 340 peerMap := make(map[uint16]*Peer, len(pm.peers)) 341 for raftId, peer := range pm.peers { 342 peerMap[raftId] = peer 343 } 344 pm.mu.RUnlock() 345 346 for { 347 for peerId, peer := range peerMap { 348 log.Info("synchronizing with peer", "peer id", peerId, "hash", hash) 349 350 peerId := peer.p2pNode.ID().String() 351 peerIdPrefix := fmt.Sprintf("%x", peer.p2pNode.ID().Bytes()[:8]) 352 353 if err := pm.downloader.Synchronise(peerIdPrefix, hash, big.NewInt(0), downloader.BoundedFullSync); err != nil { 354 log.Info("failed to synchronize with peer", "peer id", peerId) 355 356 time.Sleep(500 * time.Millisecond) 357 } else { 358 return 359 } 360 } 361 } 362 } 363 364 func (pm *ProtocolManager) logNewlyAcceptedTransactions(preSyncHead *types.Block) { 365 newHead := pm.blockchain.CurrentBlock() 366 numBlocks := newHead.NumberU64() - preSyncHead.NumberU64() 367 blocks := make([]*types.Block, numBlocks) 368 currBlock := newHead 369 blocksSeen := 0 370 for currBlock.Hash() != preSyncHead.Hash() { 371 blocks[int(numBlocks)-(1+blocksSeen)] = currBlock 372 373 blocksSeen += 1 374 currBlock = pm.blockchain.GetBlockByHash(currBlock.ParentHash()) 375 } 376 for _, block := range blocks { 377 for _, tx := range block.Transactions() { 378 log.EmitCheckpoint(log.TxAccepted, "tx", tx.Hash().Hex()) 379 } 380 } 381 }