cmd/devp2p: build forkchoice update in code

This commit is contained in:
healthykim 2026-04-15 17:20:55 +02:00
parent 8389557759
commit 80e67a75d1
2 changed files with 48 additions and 27 deletions

View file

@ -18,31 +18,32 @@ package ethtest
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"os"
"path/filepath"
"time" "time"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
// EngineClient is a wrapper around engine-related data. // EngineClient is a wrapper around engine-related data.
type EngineClient struct { type EngineClient struct {
url string url string
jwt [32]byte jwt [32]byte
headfcu []byte chain *Chain
http *http.Client
} }
// NewEngineClient creates a new engine client. // NewEngineClient creates a new engine client.
func NewEngineClient(dir, url, jwt string) (*EngineClient, error) { func NewEngineClient(url, jwtSecret string, chain *Chain) *EngineClient {
headfcu, err := os.ReadFile(filepath.Join(dir, "headfcu.json")) return &EngineClient{
if err != nil { url: url,
return nil, fmt.Errorf("failed to read headfcu: %w", err) jwt: common.HexToHash(jwtSecret),
chain: chain,
http: &http.Client{Timeout: 10 * time.Second},
} }
return &EngineClient{url, common.HexToHash(jwt), headfcu}, nil
} }
// token returns the jwt claim token for authorization. // token returns the jwt claim token for authorization.
@ -52,18 +53,41 @@ func (ec *EngineClient) token() string {
return token return token
} }
// sendForkchoiceUpdated sends an fcu for the head of the generated chain. // rpcRequest marshals a JSON-RPC 2.0 request body for the given method and params.
func (ec *EngineClient) sendForkchoiceUpdated() error { func rpcRequest(method string, params ...any) ([]byte, error) {
var ( p, err := json.Marshal(params)
req, _ = http.NewRequest(http.MethodPost, ec.url, io.NopCloser(bytes.NewReader(ec.headfcu))) if err != nil {
header = make(http.Header) return nil, err
) }
// Set header return fmt.Appendf(nil, `{"jsonrpc":"2.0","id":1,"method":%q,"params":%s}`, method, p), nil
header.Set("accept", "application/json") }
header.Set("content-type", "application/json")
header.Set("Authorization", fmt.Sprintf("Bearer %v", ec.token()))
req.Header = header
_, err := new(http.Client).Do(req) // call sends an authenticated Engine API JSON-RPC request. Response body is
// not inspected — only transport errors are returned.
func (ec *EngineClient) call(method string, params ...any) error {
body, err := rpcRequest(method, params...)
if err != nil {
return err
}
req, err := http.NewRequest(http.MethodPost, ec.url, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("accept", "application/json")
req.Header.Set("content-type", "application/json")
req.Header.Set("Authorization", "Bearer "+ec.token())
_, err = ec.http.Do(req)
return err return err
} }
// sendForkchoiceUpdated sends an fcu for the head of the generated chain.
func (ec *EngineClient) sendForkchoiceUpdated() error {
head := ec.chain.Head().Hash()
state := engine.ForkchoiceStateV1{
HeadBlockHash: head,
SafeBlockHash: head,
FinalizedBlockHash: head,
}
return ec.call("engine_forkchoiceUpdatedV3", state, nil)
}

View file

@ -56,10 +56,7 @@ func NewSuite(dest *enode.Node, chainDir, engineURL, jwt string) (*Suite, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
engine, err := NewEngineClient(chainDir, engineURL, jwt) engine := NewEngineClient(engineURL, jwt, chain)
if err != nil {
return nil, err
}
return &Suite{ return &Suite{
Dest: dest, Dest: dest,