sync.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2016 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. package les
  17. import (
  18. "context"
  19. "errors"
  20. "time"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/core/rawdb"
  23. "github.com/ethereum/go-ethereum/eth/downloader"
  24. "github.com/ethereum/go-ethereum/light"
  25. "github.com/ethereum/go-ethereum/log"
  26. )
  27. var errInvalidCheckpoint = errors.New("invalid advertised checkpoint")
  28. const (
  29. // lightSync starts syncing from the current highest block.
  30. // If the chain is empty, syncing the entire header chain.
  31. lightSync = iota
  32. // legacyCheckpointSync starts syncing from a hardcoded checkpoint.
  33. legacyCheckpointSync
  34. // checkpointSync starts syncing from a checkpoint signed by trusted
  35. // signer or hardcoded checkpoint for compatibility.
  36. checkpointSync
  37. )
  38. // validateCheckpoint verifies the advertised checkpoint by peer is valid or not.
  39. //
  40. // Each network has several hard-coded checkpoint signer addresses. Only the
  41. // checkpoint issued by the specified signer is considered valid.
  42. //
  43. // In addition to the checkpoint registered in the registrar contract, there are
  44. // several legacy hardcoded checkpoints in our codebase. These checkpoints are
  45. // also considered as valid.
  46. func (h *clientHandler) validateCheckpoint(peer *serverPeer) error {
  47. ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
  48. defer cancel()
  49. // Fetch the block header corresponding to the checkpoint registration.
  50. cp := peer.checkpoint
  51. header, err := light.GetUntrustedHeaderByNumber(ctx, h.backend.odr, peer.checkpointNumber, peer.id)
  52. if err != nil {
  53. return err
  54. }
  55. // Fetch block logs associated with the block header.
  56. logs, err := light.GetUntrustedBlockLogs(ctx, h.backend.odr, header)
  57. if err != nil {
  58. return err
  59. }
  60. events := h.backend.oracle.Contract().LookupCheckpointEvents(logs, cp.SectionIndex, cp.Hash())
  61. if len(events) == 0 {
  62. return errInvalidCheckpoint
  63. }
  64. var (
  65. index = events[0].Index
  66. hash = events[0].CheckpointHash
  67. signatures [][]byte
  68. )
  69. for _, event := range events {
  70. signatures = append(signatures, append(event.R[:], append(event.S[:], event.V)...))
  71. }
  72. valid, signers := h.backend.oracle.VerifySigners(index, hash, signatures)
  73. if !valid {
  74. return errInvalidCheckpoint
  75. }
  76. log.Warn("Verified advertised checkpoint", "peer", peer.id, "signers", len(signers))
  77. return nil
  78. }
  79. // synchronise tries to sync up our local chain with a remote peer.
  80. func (h *clientHandler) synchronise(peer *serverPeer) {
  81. // Short circuit if the peer is nil.
  82. if peer == nil {
  83. return
  84. }
  85. // Make sure the peer's TD is higher than our own.
  86. latest := h.backend.blockchain.CurrentHeader()
  87. currentTd := rawdb.ReadTd(h.backend.chainDb, latest.Hash(), latest.Number.Uint64())
  88. if currentTd != nil && peer.Td().Cmp(currentTd) < 0 {
  89. return
  90. }
  91. // Recap the checkpoint.
  92. //
  93. // The light client may be connected to several different versions of the server.
  94. // (1) Old version server which can not provide stable checkpoint in the handshake packet.
  95. // => Use hardcoded checkpoint or empty checkpoint
  96. // (2) New version server but simple checkpoint syncing is not enabled(e.g. mainnet, new testnet or private network)
  97. // => Use hardcoded checkpoint or empty checkpoint
  98. // (3) New version server but the provided stable checkpoint is even lower than the hardcoded one.
  99. // => Use hardcoded checkpoint
  100. // (4) New version server with valid and higher stable checkpoint
  101. // => Use provided checkpoint
  102. var checkpoint = &peer.checkpoint
  103. var hardcoded bool
  104. if h.checkpoint != nil && h.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex {
  105. checkpoint = h.checkpoint // Use the hardcoded one.
  106. hardcoded = true
  107. }
  108. // Determine whether we should run checkpoint syncing or normal light syncing.
  109. //
  110. // Here has four situations that we will disable the checkpoint syncing:
  111. //
  112. // 1. The checkpoint is empty
  113. // 2. The latest head block of the local chain is above the checkpoint.
  114. // 3. The checkpoint is hardcoded(recap with local hardcoded checkpoint)
  115. // 4. For some networks the checkpoint syncing is not activated.
  116. mode := checkpointSync
  117. switch {
  118. case checkpoint.Empty():
  119. mode = lightSync
  120. log.Debug("Disable checkpoint syncing", "reason", "empty checkpoint")
  121. case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*h.backend.iConfig.ChtSize-1:
  122. mode = lightSync
  123. log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint")
  124. case hardcoded:
  125. mode = legacyCheckpointSync
  126. log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded")
  127. case h.backend.oracle == nil || !h.backend.oracle.IsRunning():
  128. if h.checkpoint == nil {
  129. mode = lightSync // Downgrade to light sync unfortunately.
  130. } else {
  131. checkpoint = h.checkpoint
  132. mode = legacyCheckpointSync
  133. }
  134. log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated")
  135. }
  136. // Notify testing framework if syncing has completed(for testing purpose).
  137. defer func() {
  138. if h.syncDone != nil {
  139. h.syncDone()
  140. }
  141. }()
  142. start := time.Now()
  143. if mode == checkpointSync || mode == legacyCheckpointSync {
  144. // Validate the advertised checkpoint
  145. if mode == checkpointSync {
  146. if err := h.validateCheckpoint(peer); err != nil {
  147. log.Debug("Failed to validate checkpoint", "reason", err)
  148. h.removePeer(peer.id)
  149. return
  150. }
  151. h.backend.blockchain.AddTrustedCheckpoint(checkpoint)
  152. }
  153. log.Debug("Checkpoint syncing start", "peer", peer.id, "checkpoint", checkpoint.SectionIndex)
  154. // Fetch the start point block header.
  155. //
  156. // For the ethash consensus engine, the start header is the block header
  157. // of the checkpoint.
  158. //
  159. // For the clique consensus engine, the start header is the block header
  160. // of the latest epoch covered by checkpoint.
  161. ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
  162. defer cancel()
  163. if !checkpoint.Empty() && !h.backend.blockchain.SyncCheckpoint(ctx, checkpoint) {
  164. log.Debug("Sync checkpoint failed")
  165. h.removePeer(peer.id)
  166. return
  167. }
  168. }
  169. // Fetch the remaining block headers based on the current chain header.
  170. if err := h.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil {
  171. log.Debug("Synchronise failed", "reason", err)
  172. return
  173. }
  174. log.Debug("Synchronise finished", "elapsed", common.PrettyDuration(time.Since(start)))
  175. }