github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/blsync/engineclient.go (about) 1 // Copyright 2024 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 17 package blsync 18 19 import ( 20 "context" 21 "strings" 22 "sync" 23 "time" 24 25 "github.com/ethereum/go-ethereum/beacon/engine" 26 "github.com/ethereum/go-ethereum/beacon/types" 27 "github.com/ethereum/go-ethereum/common" 28 ctypes "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/log" 30 "github.com/ethereum/go-ethereum/rpc" 31 ) 32 33 type engineClient struct { 34 config *lightClientConfig 35 rpc *rpc.Client 36 rootCtx context.Context 37 cancelRoot context.CancelFunc 38 wg sync.WaitGroup 39 } 40 41 func startEngineClient(config *lightClientConfig, rpc *rpc.Client, headCh <-chan types.ChainHeadEvent) *engineClient { 42 ctx, cancel := context.WithCancel(context.Background()) 43 ec := &engineClient{ 44 config: config, 45 rpc: rpc, 46 rootCtx: ctx, 47 cancelRoot: cancel, 48 } 49 ec.wg.Add(1) 50 go ec.updateLoop(headCh) 51 return ec 52 } 53 54 func (ec *engineClient) stop() { 55 ec.cancelRoot() 56 ec.wg.Wait() 57 } 58 59 func (ec *engineClient) updateLoop(headCh <-chan types.ChainHeadEvent) { 60 defer ec.wg.Done() 61 62 for { 63 select { 64 case <-ec.rootCtx.Done(): 65 log.Debug("Stopping engine API update loop") 66 return 67 68 case event := <-headCh: 69 if ec.rpc == nil { // dry run, no engine API specified 70 log.Info("New execution block retrieved", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "finalized", event.Finalized) 71 continue 72 } 73 74 fork := ec.config.ForkAtEpoch(event.BeaconHead.Epoch()) 75 forkName := strings.ToLower(fork.Name) 76 77 log.Debug("Calling NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash()) 78 if status, err := ec.callNewPayload(forkName, event); err == nil { 79 log.Info("Successful NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "status", status) 80 } else { 81 log.Error("Failed NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "error", err) 82 } 83 84 log.Debug("Calling ForkchoiceUpdated", "head", event.Block.Hash()) 85 if status, err := ec.callForkchoiceUpdated(forkName, event); err == nil { 86 log.Info("Successful ForkchoiceUpdated", "head", event.Block.Hash(), "status", status) 87 } else { 88 log.Error("Failed ForkchoiceUpdated", "head", event.Block.Hash(), "error", err) 89 } 90 } 91 } 92 } 93 94 func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent) (string, error) { 95 execData := engine.BlockToExecutableData(event.Block, nil, nil).ExecutionPayload 96 97 var ( 98 method string 99 params = []any{execData} 100 ) 101 switch fork { 102 case "deneb": 103 method = "engine_newPayloadV3" 104 parentBeaconRoot := event.BeaconHead.ParentRoot 105 blobHashes := collectBlobHashes(event.Block) 106 params = append(params, blobHashes, parentBeaconRoot) 107 case "capella": 108 method = "engine_newPayloadV2" 109 default: 110 method = "engine_newPayloadV1" 111 } 112 113 ctx, cancel := context.WithTimeout(ec.rootCtx, time.Second*5) 114 defer cancel() 115 var resp engine.PayloadStatusV1 116 err := ec.rpc.CallContext(ctx, &resp, method, params...) 117 return resp.Status, err 118 } 119 120 func collectBlobHashes(b *ctypes.Block) []common.Hash { 121 list := make([]common.Hash, 0) 122 for _, tx := range b.Transactions() { 123 list = append(list, tx.BlobHashes()...) 124 } 125 return list 126 } 127 128 func (ec *engineClient) callForkchoiceUpdated(fork string, event types.ChainHeadEvent) (string, error) { 129 update := engine.ForkchoiceStateV1{ 130 HeadBlockHash: event.Block.Hash(), 131 SafeBlockHash: event.Finalized, 132 FinalizedBlockHash: event.Finalized, 133 } 134 135 var method string 136 switch fork { 137 case "deneb": 138 method = "engine_forkchoiceUpdatedV3" 139 case "capella": 140 method = "engine_forkchoiceUpdatedV2" 141 default: 142 method = "engine_forkchoiceUpdatedV1" 143 } 144 145 ctx, cancel := context.WithTimeout(ec.rootCtx, time.Second*5) 146 defer cancel() 147 var resp engine.ForkChoiceResponse 148 err := ec.rpc.CallContext(ctx, &resp, method, update, nil) 149 return resp.PayloadStatus.Status, err 150 }