sync.go 7.4 KB

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