api.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright 2022 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 catalyst implements the temporary eth1/eth2 RPC integration.
  17. package catalyst
  18. import (
  19. "errors"
  20. "fmt"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/core/beacon"
  23. "github.com/ethereum/go-ethereum/les"
  24. "github.com/ethereum/go-ethereum/log"
  25. "github.com/ethereum/go-ethereum/node"
  26. "github.com/ethereum/go-ethereum/rpc"
  27. )
  28. // Register adds catalyst APIs to the light client.
  29. func Register(stack *node.Node, backend *les.LightEthereum) error {
  30. log.Warn("Catalyst mode enabled", "protocol", "les")
  31. stack.RegisterAPIs([]rpc.API{
  32. {
  33. Namespace: "engine",
  34. Service: NewConsensusAPI(backend),
  35. Authenticated: true,
  36. },
  37. })
  38. return nil
  39. }
  40. type ConsensusAPI struct {
  41. les *les.LightEthereum
  42. }
  43. // NewConsensusAPI creates a new consensus api for the given backend.
  44. // The underlying blockchain needs to have a valid terminal total difficulty set.
  45. func NewConsensusAPI(les *les.LightEthereum) *ConsensusAPI {
  46. if les.BlockChain().Config().TerminalTotalDifficulty == nil {
  47. log.Warn("Catalyst started without valid total difficulty")
  48. }
  49. return &ConsensusAPI{les: les}
  50. }
  51. // ForkchoiceUpdatedV1 has several responsibilities:
  52. // If the method is called with an empty head block:
  53. // we return success, which can be used to check if the catalyst mode is enabled
  54. // If the total difficulty was not reached:
  55. // we return INVALID
  56. // If the finalizedBlockHash is set:
  57. // we check if we have the finalizedBlockHash in our db, if not we start a sync
  58. // We try to set our blockchain to the headBlock
  59. // If there are payloadAttributes:
  60. // we return an error since block creation is not supported in les mode
  61. func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
  62. if heads.HeadBlockHash == (common.Hash{}) {
  63. log.Warn("Forkchoice requested update to zero hash")
  64. return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
  65. }
  66. if err := api.checkTerminalTotalDifficulty(heads.HeadBlockHash); err != nil {
  67. if header := api.les.BlockChain().GetHeaderByHash(heads.HeadBlockHash); header == nil {
  68. // TODO (MariusVanDerWijden) trigger sync
  69. return beacon.STATUS_SYNCING, nil
  70. }
  71. return beacon.STATUS_INVALID, err
  72. }
  73. // If the finalized block is set, check if it is in our blockchain
  74. if heads.FinalizedBlockHash != (common.Hash{}) {
  75. if header := api.les.BlockChain().GetHeaderByHash(heads.FinalizedBlockHash); header == nil {
  76. // TODO (MariusVanDerWijden) trigger sync
  77. return beacon.STATUS_SYNCING, nil
  78. }
  79. }
  80. // SetHead
  81. if err := api.setCanonical(heads.HeadBlockHash); err != nil {
  82. return beacon.STATUS_INVALID, err
  83. }
  84. if payloadAttributes != nil {
  85. return beacon.STATUS_INVALID, errors.New("not supported")
  86. }
  87. return api.validForkChoiceResponse(), nil
  88. }
  89. // GetPayloadV1 returns a cached payload by id. It's not supported in les mode.
  90. func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) {
  91. return nil, beacon.GenericServerError.With(errors.New("not supported in light client mode"))
  92. }
  93. // ExecutePayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
  94. func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) {
  95. block, err := beacon.ExecutableDataToBlock(params)
  96. if err != nil {
  97. return api.invalid(), err
  98. }
  99. if !api.les.BlockChain().HasHeader(block.ParentHash(), block.NumberU64()-1) {
  100. /*
  101. TODO (MariusVanDerWijden) reenable once sync is merged
  102. if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), block.Header()); err != nil {
  103. return SYNCING, err
  104. }
  105. */
  106. // TODO (MariusVanDerWijden) we should return nil here not empty hash
  107. return beacon.PayloadStatusV1{Status: beacon.SYNCING, LatestValidHash: nil}, nil
  108. }
  109. parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash)
  110. if parent == nil {
  111. return api.invalid(), fmt.Errorf("could not find parent %x", params.ParentHash)
  112. }
  113. td := api.les.BlockChain().GetTd(parent.Hash(), block.NumberU64()-1)
  114. ttd := api.les.BlockChain().Config().TerminalTotalDifficulty
  115. if td.Cmp(ttd) < 0 {
  116. return api.invalid(), fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd)
  117. }
  118. if err = api.les.BlockChain().InsertHeader(block.Header()); err != nil {
  119. return api.invalid(), err
  120. }
  121. if merger := api.les.Merger(); !merger.TDDReached() {
  122. merger.ReachTTD()
  123. }
  124. hash := block.Hash()
  125. return beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &hash}, nil
  126. }
  127. func (api *ConsensusAPI) validForkChoiceResponse() beacon.ForkChoiceResponse {
  128. currentHash := api.les.BlockChain().CurrentHeader().Hash()
  129. return beacon.ForkChoiceResponse{
  130. PayloadStatus: beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &currentHash},
  131. }
  132. }
  133. // invalid returns a response "INVALID" with the latest valid hash set to the current head.
  134. func (api *ConsensusAPI) invalid() beacon.PayloadStatusV1 {
  135. currentHash := api.les.BlockChain().CurrentHeader().Hash()
  136. return beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: &currentHash}
  137. }
  138. func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
  139. // shortcut if we entered PoS already
  140. if api.les.Merger().PoSFinalized() {
  141. return nil
  142. }
  143. // make sure the parent has enough terminal total difficulty
  144. header := api.les.BlockChain().GetHeaderByHash(head)
  145. if header == nil {
  146. return errors.New("unknown header")
  147. }
  148. td := api.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
  149. if td != nil && td.Cmp(api.les.BlockChain().Config().TerminalTotalDifficulty) < 0 {
  150. return errors.New("invalid ttd")
  151. }
  152. return nil
  153. }
  154. // setCanonical is called to perform a force choice.
  155. func (api *ConsensusAPI) setCanonical(newHead common.Hash) error {
  156. log.Info("Setting head", "head", newHead)
  157. headHeader := api.les.BlockChain().CurrentHeader()
  158. if headHeader.Hash() == newHead {
  159. return nil
  160. }
  161. newHeadHeader := api.les.BlockChain().GetHeaderByHash(newHead)
  162. if newHeadHeader == nil {
  163. return errors.New("unknown header")
  164. }
  165. if err := api.les.BlockChain().SetCanonical(newHeadHeader); err != nil {
  166. return err
  167. }
  168. // Trigger the transition if it's the first `NewHead` event.
  169. if merger := api.les.Merger(); !merger.PoSFinalized() {
  170. merger.FinalizePoS()
  171. }
  172. return nil
  173. }