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  }