Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ tmp
report.txt

# execution client data
execution/
/execution/
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM golang:1.25-bookworm AS builder

RUN apt-get update && apt-get install -y --no-install-recommends \
git ca-certificates gcc g++ libc6-dev && \
rm -rf /var/lib/apt/lists/*

WORKDIR /prysm
COPY go.mod go.sum ./
COPY third_party/ third_party/
RUN go mod download

COPY . .
RUN CGO_ENABLED=1 go build -o /usr/local/bin/beacon-chain ./cmd/beacon-chain
RUN CGO_ENABLED=1 go build -o /usr/local/bin/validator ./cmd/validator

FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates && \
rm -rf /var/lib/apt/lists/*

COPY --from=builder /usr/local/bin/beacon-chain /beacon-chain
COPY --from=builder /usr/local/bin/validator /validator

ENTRYPOINT ["/beacon-chain"]
23 changes: 23 additions & 0 deletions Dockerfile.validator
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM golang:1.25-bookworm AS builder

RUN apt-get update && apt-get install -y --no-install-recommends \
git ca-certificates gcc g++ libc6-dev && \
rm -rf /var/lib/apt/lists/*

WORKDIR /prysm
COPY go.mod go.sum ./
COPY third_party/ third_party/
RUN go mod download

COPY . .
RUN CGO_ENABLED=1 go build -o /usr/local/bin/validator ./cmd/validator

FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates && \
rm -rf /var/lib/apt/lists/*

COPY --from=builder /usr/local/bin/validator /validator

ENTRYPOINT ["/validator"]
90 changes: 82 additions & 8 deletions beacon-chain/execution/engine_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ type EngineCaller interface {

var ErrEmptyBlockHash = errors.New("Block hash is empty 0x0000...")

// NewPayload request calls the engine_newPayloadVX method via JSON-RPC.
// NewPayload request calls the engine_newPayloadVX method.
// Per EIP-8161, it prefers SSZ-REST transport when available, falling back to JSON-RPC.
func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionData, versionedHashes []common.Hash, parentBlockRoot *common.Hash, executionRequests *pb.ExecutionRequests) ([]byte, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.NewPayload")
defer span.End()
Expand All @@ -164,6 +165,16 @@ func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionDa
d := time.Now().Add(time.Duration(params.BeaconConfig().ExecutionEngineTimeoutValue) * time.Second)
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()

// EIP-8161: Try SSZ-REST first if available, fall back to JSON-RPC on any error.
if s.isSSZRestAvailable() {
hash, err := s.newPayloadSSZRest(ctx, payload, versionedHashes, parentBlockRoot, executionRequests)
if err == nil {
return hash, nil
}
log.WithError(err).Warn("SSZ-REST new_payload failed, falling back to JSON-RPC")
}

result := &pb.PayloadStatus{}

switch payloadPb := payload.Proto().(type) {
Expand Down Expand Up @@ -216,7 +227,8 @@ func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionDa
}
}

// ForkchoiceUpdated calls the engine_forkchoiceUpdatedV1 method via JSON-RPC.
// ForkchoiceUpdated calls the engine_forkchoiceUpdatedVX method.
// Per EIP-8161, it prefers SSZ-REST transport when available, falling back to JSON-RPC.
func (s *Service) ForkchoiceUpdated(
ctx context.Context, state *pb.ForkchoiceState, attrs payloadattribute.Attributer,
) (*pb.PayloadIDBytes, []byte, error) {
Expand All @@ -230,11 +242,22 @@ func (s *Service) ForkchoiceUpdated(
d := time.Now().Add(time.Duration(params.BeaconConfig().ExecutionEngineTimeoutValue) * time.Second)
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()
result := &ForkchoiceUpdatedResponse{}

if attrs == nil {
return nil, nil, errors.New("nil payload attributer")
}

// EIP-8161: Try SSZ-REST first if available, fall back to JSON-RPC on any error.
if s.isSSZRestAvailable() {
pid, hash, err := s.forkchoiceUpdatedSSZRest(ctx, state, attrs)
if err == nil {
return pid, hash, nil
}
log.WithError(err).Warn("SSZ-REST forkchoice_updated failed, falling back to JSON-RPC")
}

result := &ForkchoiceUpdatedResponse{}

switch attrs.Version() {
case version.Bellatrix:
a, err := attrs.PbV1()
Expand Down Expand Up @@ -303,8 +326,8 @@ func getPayloadMethodAndMessage(slot primitives.Slot) (string, proto.Message) {
return GetPayloadMethod, &pb.ExecutionPayload{}
}

// GetPayload calls the engine_getPayloadVX method via JSON-RPC.
// It returns the execution data as well as the blobs bundle.
// GetPayload calls the engine_getPayloadVX method.
// Per EIP-8161, it prefers SSZ-REST transport when available, falling back to JSON-RPC.
func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (*blocks.GetPayloadResponse, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetPayload")
defer span.End()
Expand All @@ -316,6 +339,15 @@ func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primit
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()

// EIP-8161: Try SSZ-REST first if available, fall back to JSON-RPC on any error.
if s.isSSZRestAvailable() {
res, err := s.getPayloadSSZRest(ctx, payloadId, slot)
if err == nil {
return res, nil
}
log.WithError(err).Warn("SSZ-REST get_payload failed, falling back to JSON-RPC")
}

method, result := getPayloadMethodAndMessage(slot)
err := s.rpcClient.CallContext(ctx, result, method, pb.PayloadIDBytes(payloadId))
if err != nil {
Expand All @@ -328,6 +360,9 @@ func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primit
return res, nil
}

// ExchangeCapabilities calls engine_exchangeCapabilitiesV2 (EIP-8160) first,
// falling back to engine_exchangeCapabilities (V1) if V2 is not supported.
// V2 also returns supportedProtocols, which we use to discover SSZ-REST endpoints.
func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeCapabilities")
defer span.End()
Expand All @@ -340,9 +375,24 @@ func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {
supportedEngineEndpoints = append(supportedEngineEndpoints, fuluEngineEndpoints...)
}

elSupportedEndpointsSlice := make([]string, len(supportedEngineEndpoints))
if err := s.rpcClient.CallContext(ctx, &elSupportedEndpointsSlice, ExchangeCapabilities, supportedEngineEndpoints); err != nil {
return nil, handleRPCError(err)
var elSupportedEndpointsSlice []string

// EIP-8161: Try SSZ-REST first if available, fall back to JSON-RPC on any error.
if s.isSSZRestAvailable() {
result, err := s.exchangeCapabilitiesSSZRest(ctx, supportedEngineEndpoints)
if err == nil {
elSupportedEndpointsSlice = result
} else {
log.WithError(err).Warn("SSZ-REST exchange_capabilities failed, falling back to JSON-RPC")
}
}

if elSupportedEndpointsSlice == nil {
// Just use V1 exchange capabilities.
err := s.rpcClient.CallContext(ctx, &elSupportedEndpointsSlice, ExchangeCapabilities, supportedEngineEndpoints)
if err != nil {
return nil, handleRPCError(err)
}
}

elSupportedEndpoints := make(map[string]bool, len(elSupportedEndpointsSlice))
Expand Down Expand Up @@ -547,6 +597,7 @@ func (s *Service) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H
}

// GetBlobs returns the blob and proof from the execution engine for the given versioned hashes.
// Per EIP-8161, it prefers SSZ-REST transport when available, falling back to JSON-RPC.
func (s *Service) GetBlobs(ctx context.Context, versionedHashes []common.Hash) ([]*pb.BlobAndProof, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetBlobs")
defer span.End()
Expand All @@ -556,6 +607,15 @@ func (s *Service) GetBlobs(ctx context.Context, versionedHashes []common.Hash) (
return nil, errors.New(fmt.Sprintf("%s is not supported", GetBlobsV1))
}

// EIP-8161: Try SSZ-REST first if available, fall back to JSON-RPC on any error.
if s.isSSZRestAvailable() {
result, err := s.getBlobsSSZRest(ctx, versionedHashes)
if err == nil {
return result, nil
}
log.WithError(err).Warn("SSZ-REST get_blobs failed, falling back to JSON-RPC")
}

result := make([]*pb.BlobAndProof, len(versionedHashes))
err := s.rpcClient.CallContext(ctx, &result, GetBlobsV1, versionedHashes)
return result, handleRPCError(err)
Expand Down Expand Up @@ -585,10 +645,24 @@ func (s *Service) GetBlobsV2(ctx context.Context, versionedHashes []common.Hash)
return result, handleRPCError(err)
}

// GetClientVersionV1 calls engine_getClientVersionV1.
// Per EIP-8161, it prefers SSZ-REST transport when available, falling back to JSON-RPC.
func (s *Service) GetClientVersionV1(ctx context.Context) ([]*structs.ClientVersionV1, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetClientVersionV1")
defer span.End()

// EIP-8161: Try SSZ-REST first if available, fall back to JSON-RPC on any error.
if s.isSSZRestAvailable() {
result, err := s.getClientVersionSSZRest(ctx)
if err == nil {
if len(result) == 0 {
return nil, errors.New("execution client returned no result")
}
return result, nil
}
log.WithError(err).Warn("SSZ-REST get_client_version failed, falling back to JSON-RPC")
}

commit := version.GitCommit()
if len(commit) >= 8 {
commit = commit[:8]
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/execution/rpc_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func (s *Service) pollConnectionStatus(ctx context.Context) {
errorLogger(err, "Could not exchange capabilities with execution client")
}
s.capabilityCache.save(c)
s.setupSSZRestClient()

return
case <-s.ctx.Done():
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/execution/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ type Service struct {
verifierWaiter *verification.InitializerWaiter
blobVerifier verification.NewBlobVerifier
capabilityCache *capabilityCache
sszRestClient *sszRestClient
graffitiInfo *GraffitiInfo
}

Expand Down
Loading