mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
28 Commits
3e7cd8c2f1
...
graffiti-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0f925a673 | ||
|
|
fcc1795497 | ||
|
|
433054d82a | ||
|
|
0be2dad39b | ||
|
|
43a66e25ef | ||
|
|
e1a292a6a3 | ||
|
|
e408dad694 | ||
|
|
60f8308145 | ||
|
|
d7983c1558 | ||
|
|
6d49931a6d | ||
|
|
fda3fb272a | ||
|
|
17a1b9668d | ||
|
|
6373d6017d | ||
|
|
fd25e8a14d | ||
|
|
3a3f05987e | ||
|
|
6cb3677541 | ||
|
|
78235a4d91 | ||
|
|
57f011c4ab | ||
|
|
cba493062c | ||
|
|
b5bd8f6b12 | ||
|
|
2d624fb0e1 | ||
|
|
64d7d546ef | ||
|
|
2de004c3d5 | ||
|
|
23b5e2e174 | ||
|
|
bfbd4d176e | ||
|
|
78995a2d8f | ||
|
|
51db2c2129 | ||
|
|
000c367a53 |
@@ -8,6 +8,7 @@ go_library(
|
||||
"deposit.go",
|
||||
"engine_client.go",
|
||||
"errors.go",
|
||||
"graffiti_info.go",
|
||||
"log.go",
|
||||
"log_processing.go",
|
||||
"metrics.go",
|
||||
@@ -88,6 +89,7 @@ go_test(
|
||||
"engine_client_fuzz_test.go",
|
||||
"engine_client_test.go",
|
||||
"execution_chain_test.go",
|
||||
"graffiti_info_test.go",
|
||||
"init_test.go",
|
||||
"log_processing_test.go",
|
||||
"mock_test.go",
|
||||
|
||||
@@ -60,7 +60,17 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// ClientVersionV1 represents the response from engine_getClientVersionV1.
|
||||
type ClientVersionV1 struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Commit string `json:"commit"`
|
||||
}
|
||||
|
||||
const (
|
||||
// GetClientVersionMethod is the engine_getClientVersionV1 method for JSON-RPC.
|
||||
GetClientVersionMethod = "engine_getClientVersionV1"
|
||||
// NewPayloadMethod v1 request string for JSON-RPC.
|
||||
NewPayloadMethod = "engine_newPayloadV1"
|
||||
// NewPayloadMethodV2 v2 request string for JSON-RPC.
|
||||
@@ -349,6 +359,27 @@ func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {
|
||||
return elSupportedEndpointsSlice, nil
|
||||
}
|
||||
|
||||
// GetClientVersion calls engine_getClientVersionV1 to retrieve EL client information.
|
||||
func (s *Service) GetClientVersion(ctx context.Context) ([]ClientVersionV1, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetClientVersion")
|
||||
defer span.End()
|
||||
|
||||
// Per spec, we send our own client info as the parameter
|
||||
clVersion := ClientVersionV1{
|
||||
Code: CLCode,
|
||||
Name: "Prysm",
|
||||
Version: version.SemanticVersion(),
|
||||
Commit: version.GetCommitPrefix(),
|
||||
}
|
||||
|
||||
var result []ClientVersionV1
|
||||
err := s.rpcClient.CallContext(ctx, &result, GetClientVersionMethod, clVersion)
|
||||
if err != nil {
|
||||
return nil, handleRPCError(err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetTerminalBlockHash returns the valid terminal block hash based on total difficulty.
|
||||
//
|
||||
// Spec code:
|
||||
|
||||
107
beacon-chain/execution/graffiti_info.go
Normal file
107
beacon-chain/execution/graffiti_info.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
||||
)
|
||||
|
||||
const (
|
||||
// CLCode is the two-letter client code for Prysm.
|
||||
CLCode = "PR"
|
||||
)
|
||||
|
||||
// GraffitiInfo holds version information for generating block graffiti.
|
||||
// It is thread-safe and can be updated by the execution service and read by the validator server.
|
||||
type GraffitiInfo struct {
|
||||
mu sync.RWMutex
|
||||
elCode string // From engine_getClientVersionV1
|
||||
elCommit string // From engine_getClientVersionV1
|
||||
}
|
||||
|
||||
// NewGraffitiInfo creates a new GraffitiInfo.
|
||||
func NewGraffitiInfo() *GraffitiInfo {
|
||||
return &GraffitiInfo{}
|
||||
}
|
||||
|
||||
// UpdateFromEngine updates the EL client information.
|
||||
func (g *GraffitiInfo) UpdateFromEngine(code, commit string) {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
g.elCode = code
|
||||
g.elCommit = commit
|
||||
}
|
||||
|
||||
// GenerateGraffiti generates graffiti using the flexible standard
|
||||
// with the provided user graffiti from the validator client request.
|
||||
// It packs as much client info as space allows, followed by a space and user graffiti.
|
||||
//
|
||||
// Available Space | Format (space added before user graffiti if present)
|
||||
// ≥13 bytes | EL(2)+commit(4)+CL(2)+commit(4)+space+user e.g. "GEabcdPRxxxx Sushi"
|
||||
// 9-12 bytes | EL(2)+commit(2)+CL(2)+commit(2)+space+user e.g. "GEabPRxx Sushi"
|
||||
// 5-8 bytes | EL(2)+CL(2)+space+user e.g. "GEPR Sushi"
|
||||
// 3-4 bytes | code(2)+space+user e.g. "GE Sushi" or "PR Sushi"
|
||||
// <3 bytes | user only e.g. "Sushi"
|
||||
func (g *GraffitiInfo) GenerateGraffiti(userGraffiti []byte) [32]byte {
|
||||
g.mu.RLock()
|
||||
defer g.mu.RUnlock()
|
||||
|
||||
var result [32]byte
|
||||
userStr := string(userGraffiti)
|
||||
// Trim trailing null bytes
|
||||
for len(userStr) > 0 && userStr[len(userStr)-1] == 0 {
|
||||
userStr = userStr[:len(userStr)-1]
|
||||
}
|
||||
|
||||
// Prepend space to user graffiti for readability
|
||||
if len(userStr) > 0 {
|
||||
userStr = " " + userStr
|
||||
}
|
||||
available := 32 - len(userStr)
|
||||
|
||||
clCommit := version.GetCommitPrefix()
|
||||
clCommit4 := truncateCommit(clCommit, 4)
|
||||
clCommit2 := truncateCommit(clCommit, 2)
|
||||
|
||||
// If no EL info, clear EL commits but still include CL info
|
||||
var elCommit4, elCommit2 string
|
||||
if g.elCode != "" {
|
||||
elCommit4 = truncateCommit(g.elCommit, 4)
|
||||
elCommit2 = truncateCommit(g.elCommit, 2)
|
||||
}
|
||||
|
||||
var graffiti string
|
||||
switch {
|
||||
case available >= 12:
|
||||
// Full: EL(2)+commit(4)+CL(2)+commit(4)+space+user
|
||||
graffiti = g.elCode + elCommit4 + CLCode + clCommit4 + userStr
|
||||
case available >= 8:
|
||||
// Reduced commits: EL(2)+commit(2)+CL(2)+commit(2)+space+user
|
||||
graffiti = g.elCode + elCommit2 + CLCode + clCommit2 + userStr
|
||||
case available >= 4:
|
||||
// Codes only: EL(2)+CL(2)+space+user
|
||||
graffiti = g.elCode + CLCode + userStr
|
||||
case available >= 2:
|
||||
// EL code only (or CL code if no EL): code(2)+space+user
|
||||
if g.elCode != "" {
|
||||
graffiti = g.elCode + userStr
|
||||
} else {
|
||||
graffiti = CLCode + userStr
|
||||
}
|
||||
default:
|
||||
// User graffiti only (no space needed since no version prefix)
|
||||
// Remove the prepended space since we can't fit any version info
|
||||
graffiti = userStr[1:]
|
||||
}
|
||||
|
||||
copy(result[:], graffiti)
|
||||
return result
|
||||
}
|
||||
|
||||
// truncateCommit returns the first n characters of the commit string.
|
||||
func truncateCommit(commit string, n int) string {
|
||||
if len(commit) <= n {
|
||||
return commit
|
||||
}
|
||||
return commit[:n]
|
||||
}
|
||||
194
beacon-chain/execution/graffiti_info_test.go
Normal file
194
beacon-chain/execution/graffiti_info_test.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
)
|
||||
|
||||
func TestGraffitiInfo_GenerateGraffiti(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
elCode string
|
||||
elCommit string
|
||||
userGraffiti []byte
|
||||
wantPrefix string
|
||||
wantSuffix string // for checking user graffiti is appended
|
||||
}{
|
||||
// No EL info cases (CL info "PR" + commit still included when space allows)
|
||||
{
|
||||
name: "No EL - empty user graffiti",
|
||||
elCode: "",
|
||||
elCommit: "",
|
||||
userGraffiti: []byte{},
|
||||
wantPrefix: "PR", // CL code, no trailing space since no user graffiti
|
||||
},
|
||||
{
|
||||
name: "No EL - short user graffiti",
|
||||
elCode: "",
|
||||
elCommit: "",
|
||||
userGraffiti: []byte("my validator"),
|
||||
wantPrefix: "PR", // CL code + commit + space + user
|
||||
wantSuffix: " my validator", // space before user graffiti
|
||||
},
|
||||
{
|
||||
name: "No EL - 28 char user graffiti (3 bytes available after space)",
|
||||
elCode: "",
|
||||
elCommit: "",
|
||||
userGraffiti: []byte("1234567890123456789012345678"), // 28 chars
|
||||
wantPrefix: "PR ", // CL code + space (3 bytes)
|
||||
},
|
||||
{
|
||||
name: "No EL - 29 char user graffiti (2 bytes available after space)",
|
||||
elCode: "",
|
||||
elCommit: "",
|
||||
userGraffiti: []byte("12345678901234567890123456789"), // 29 chars, 2 bytes available = fits PR
|
||||
wantPrefix: "PR ", // CL code (2 bytes) + space fits exactly
|
||||
},
|
||||
{
|
||||
name: "No EL - 30 char user graffiti (1 byte available after space)",
|
||||
elCode: "",
|
||||
elCommit: "",
|
||||
userGraffiti: []byte("123456789012345678901234567890"), // 30 chars, 1 byte available = not enough for code+space
|
||||
wantPrefix: "123456789012345678901234567890", // User only
|
||||
},
|
||||
{
|
||||
name: "No EL - 31 char user graffiti (0 bytes available after space)",
|
||||
elCode: "",
|
||||
elCommit: "",
|
||||
userGraffiti: []byte("1234567890123456789012345678901"),
|
||||
wantPrefix: "1234567890123456789012345678901", // User only
|
||||
},
|
||||
// With EL info - flexible standard format cases
|
||||
{
|
||||
name: "With EL - full format (empty user graffiti)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte{},
|
||||
wantPrefix: "GEabcdPR", // No trailing space when no user graffiti
|
||||
},
|
||||
{
|
||||
name: "With EL - full format (short user graffiti)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte("Bob"),
|
||||
wantPrefix: "GEabcdPR", // EL(2)+commit(4)+CL(2)+commit(4)
|
||||
wantSuffix: " Bob", // space before user graffiti
|
||||
},
|
||||
{
|
||||
name: "With EL - full format (18 char user, 13 bytes available after space)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte("123456789012345678"), // 18 chars, need 19 with space, leaves 13
|
||||
wantPrefix: "GEabcdPR", // Full format fits (12 bytes)
|
||||
},
|
||||
{
|
||||
name: "With EL - reduced commits (22 char user, 9 bytes available after space)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte("1234567890123456789012"), // 22 chars, need 23 with space, leaves 9
|
||||
wantPrefix: "GEabPR", // Reduced format (8 bytes)
|
||||
},
|
||||
{
|
||||
name: "With EL - codes only (26 char user, 5 bytes available after space)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte("12345678901234567890123456"), // 26 chars, need 27 with space, leaves 5
|
||||
wantPrefix: "GEPR ", // Codes only (4 bytes) + space
|
||||
},
|
||||
{
|
||||
name: "With EL - EL code only (28 char user, 3 bytes available after space)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte("1234567890123456789012345678"), // 28 chars, need 29 with space, leaves 3
|
||||
wantPrefix: "GE ", // EL code (2 bytes) + space
|
||||
},
|
||||
{
|
||||
name: "With EL - user only (30 char user, 1 byte available after space)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte("123456789012345678901234567890"), // 30 chars, need 31 with space, leaves 1
|
||||
wantPrefix: "123456789012345678901234567890", // Not enough for code+space, user only
|
||||
},
|
||||
{
|
||||
name: "With EL - user only (32 char user, 0 bytes available)",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: []byte("12345678901234567890123456789012"),
|
||||
wantPrefix: "12345678901234567890123456789012",
|
||||
},
|
||||
// Null byte handling
|
||||
{
|
||||
name: "Null bytes - input with trailing nulls",
|
||||
elCode: "GE",
|
||||
elCommit: "abcd1234",
|
||||
userGraffiti: append([]byte("test"), 0, 0, 0),
|
||||
wantPrefix: "GEabcdPR",
|
||||
wantSuffix: " test",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewGraffitiInfo()
|
||||
if tt.elCode != "" {
|
||||
g.UpdateFromEngine(tt.elCode, tt.elCommit)
|
||||
}
|
||||
|
||||
result := g.GenerateGraffiti(tt.userGraffiti)
|
||||
resultStr := string(result[:])
|
||||
|
||||
// Check prefix
|
||||
require.Equal(t, true, len(resultStr) >= len(tt.wantPrefix), "Result too short for prefix check")
|
||||
require.Equal(t, tt.wantPrefix, resultStr[:len(tt.wantPrefix)], "Prefix mismatch")
|
||||
|
||||
// Check suffix if specified
|
||||
if tt.wantSuffix != "" {
|
||||
trimmed := trimNullBytes(resultStr)
|
||||
require.Equal(t, true, len(trimmed) >= len(tt.wantSuffix), "Result too short for suffix check")
|
||||
require.Equal(t, tt.wantSuffix, trimmed[len(trimmed)-len(tt.wantSuffix):], "Suffix mismatch")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraffitiInfo_UpdateFromEngine(t *testing.T) {
|
||||
g := NewGraffitiInfo()
|
||||
|
||||
// Initially no EL info - should still have CL info (PR + commit)
|
||||
result := g.GenerateGraffiti([]byte{})
|
||||
resultStr := string(result[:])
|
||||
require.Equal(t, "PR", resultStr[:2], "Expected CL info before update")
|
||||
|
||||
// Update with EL info
|
||||
g.UpdateFromEngine("GE", "1234abcd")
|
||||
|
||||
result = g.GenerateGraffiti([]byte{})
|
||||
resultStr = string(result[:])
|
||||
require.Equal(t, "GE1234PR", resultStr[:8], "Expected EL+CL info after update")
|
||||
}
|
||||
|
||||
func TestTruncateCommit(t *testing.T) {
|
||||
tests := []struct {
|
||||
commit string
|
||||
n int
|
||||
want string
|
||||
}{
|
||||
{"abcd1234", 4, "abcd"},
|
||||
{"ab", 4, "ab"},
|
||||
{"", 4, ""},
|
||||
{"abcdef", 2, "ab"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := truncateCommit(tt.commit, tt.n)
|
||||
require.Equal(t, tt.want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func trimNullBytes(s string) string {
|
||||
for len(s) > 0 && s[len(s)-1] == 0 {
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -124,3 +124,11 @@ func WithVerifierWaiter(v *verification.InitializerWaiter) Option {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithGraffitiInfo sets the GraffitiInfo for client version tracking.
|
||||
func WithGraffitiInfo(g *GraffitiInfo) Option {
|
||||
return func(s *Service) error {
|
||||
s.graffitiInfo = g
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +162,7 @@ type Service struct {
|
||||
verifierWaiter *verification.InitializerWaiter
|
||||
blobVerifier verification.NewBlobVerifier
|
||||
capabilityCache *capabilityCache
|
||||
graffitiInfo *GraffitiInfo
|
||||
}
|
||||
|
||||
// NewService sets up a new instance with an ethclient when given a web3 endpoint as a string in the config.
|
||||
@@ -318,6 +319,28 @@ func (s *Service) updateConnectedETH1(state bool) {
|
||||
s.updateBeaconNodeStats()
|
||||
}
|
||||
|
||||
// GraffitiInfo returns the GraffitiInfo struct for graffiti generation.
|
||||
func (s *Service) GraffitiInfo() *GraffitiInfo {
|
||||
return s.graffitiInfo
|
||||
}
|
||||
|
||||
// updateGraffitiInfo fetches EL client version and updates the graffiti info.
|
||||
func (s *Service) updateGraffitiInfo() {
|
||||
if s.graffitiInfo == nil {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(s.ctx, time.Second)
|
||||
defer cancel()
|
||||
versions, err := s.GetClientVersion(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Could not get execution client version for graffiti")
|
||||
return
|
||||
}
|
||||
if len(versions) >= 1 {
|
||||
s.graffitiInfo.UpdateFromEngine(versions[0].Code, versions[0].Commit)
|
||||
}
|
||||
}
|
||||
|
||||
// refers to the latest eth1 block which follows the condition: eth1_timestamp +
|
||||
// SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE <= current_unix_time
|
||||
func (s *Service) followedBlockHeight(ctx context.Context) (uint64, error) {
|
||||
@@ -598,6 +621,12 @@ func (s *Service) run(done <-chan struct{}) {
|
||||
chainstartTicker := time.NewTicker(logPeriod)
|
||||
defer chainstartTicker.Stop()
|
||||
|
||||
// Update graffiti info 4 times per epoch (~96 seconds with 12s slots and 32 slots/epoch)
|
||||
graffitiTicker := time.NewTicker(96 * time.Second)
|
||||
defer graffitiTicker.Stop()
|
||||
// Initial update
|
||||
s.updateGraffitiInfo()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
@@ -622,6 +651,8 @@ func (s *Service) run(done <-chan struct{}) {
|
||||
continue
|
||||
}
|
||||
s.logTillChainStart(context.Background())
|
||||
case <-graffitiTicker.C:
|
||||
s.updateGraffitiInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,6 +775,9 @@ func (b *BeaconNode) registerPOWChainService() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create GraffitiInfo for client version tracking in block graffiti
|
||||
graffitiInfo := execution.NewGraffitiInfo()
|
||||
|
||||
// skipcq: CRT-D0001
|
||||
opts := append(
|
||||
b.serviceFlagOpts.executionChainFlagOpts,
|
||||
@@ -787,6 +790,7 @@ func (b *BeaconNode) registerPOWChainService() error {
|
||||
execution.WithFinalizedStateAtStartup(b.finalizedStateAtStartUp),
|
||||
execution.WithJwtId(b.cliCtx.String(flags.JwtId.Name)),
|
||||
execution.WithVerifierWaiter(b.verifyInitWaiter),
|
||||
execution.WithGraffitiInfo(graffitiInfo),
|
||||
)
|
||||
web3Service, err := execution.NewService(b.ctx, opts...)
|
||||
if err != nil {
|
||||
@@ -993,6 +997,7 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error {
|
||||
TrackedValidatorsCache: b.trackedValidatorsCache,
|
||||
PayloadIDCache: b.payloadIDCache,
|
||||
LCStore: b.lcStore,
|
||||
GraffitiInfo: web3Service.GraffitiInfo(),
|
||||
})
|
||||
|
||||
return b.services.RegisterService(rpcService)
|
||||
|
||||
@@ -89,7 +89,13 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
|
||||
}
|
||||
// Set slot, graffiti, randao reveal, and parent root.
|
||||
sBlk.SetSlot(req.Slot)
|
||||
sBlk.SetGraffiti(req.Graffiti)
|
||||
// Generate graffiti with client version info using flexible standard
|
||||
if vs.GraffitiInfo != nil {
|
||||
graffiti := vs.GraffitiInfo.GenerateGraffiti(req.Graffiti)
|
||||
sBlk.SetGraffiti(graffiti[:])
|
||||
} else {
|
||||
sBlk.SetGraffiti(req.Graffiti)
|
||||
}
|
||||
sBlk.SetRandaoReveal(req.RandaoReveal)
|
||||
sBlk.SetParentRoot(parentRoot[:])
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ type Server struct {
|
||||
ClockWaiter startup.ClockWaiter
|
||||
CoreService *core.Service
|
||||
AttestationStateFetcher blockchain.AttestationStateFetcher
|
||||
GraffitiInfo *execution.GraffitiInfo
|
||||
}
|
||||
|
||||
// Deprecated: The gRPC API will remain the default and fully supported through v8 (expected in 2026) but will be eventually removed in favor of REST API.
|
||||
|
||||
@@ -125,6 +125,7 @@ type Config struct {
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
LCStore *lightClient.Store
|
||||
GraffitiInfo *execution.GraffitiInfo
|
||||
}
|
||||
|
||||
// NewService instantiates a new RPC service instance that will
|
||||
@@ -256,6 +257,7 @@ func NewService(ctx context.Context, cfg *Config) *Service {
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
AttestationStateFetcher: s.cfg.AttestationReceiver,
|
||||
GraffitiInfo: s.cfg.GraffitiInfo,
|
||||
}
|
||||
s.validatorServer = validatorServer
|
||||
nodeServer := &nodev1alpha1.Server{
|
||||
|
||||
3
changelog/satushh-graffiti-impl.md
Normal file
3
changelog/satushh-graffiti-impl.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Graffiti implementation based on the design doc.
|
||||
@@ -47,3 +47,13 @@ func BuildData() string {
|
||||
}
|
||||
return fmt.Sprintf("Prysm/%s/%s", gitTag, gitCommit)
|
||||
}
|
||||
|
||||
// GetCommitPrefix returns the first 4 characters of the git commit.
|
||||
// This is used for graffiti generation per the client identification spec.
|
||||
// Note: BuildData() must be called before this (happens at startup via Version()).
|
||||
func GetCommitPrefix() string {
|
||||
if len(gitCommit) < 4 {
|
||||
return gitCommit
|
||||
}
|
||||
return gitCommit[:4]
|
||||
}
|
||||
|
||||
@@ -223,17 +223,27 @@ func verifyGraffitiInBlocks(_ *e2etypes.EvaluationContext, conns ...*grpc.Client
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var e bool
|
||||
var found bool
|
||||
slot := blk.Block().Slot()
|
||||
graffitiInBlock := blk.Block().Body().Graffiti()
|
||||
// Trim trailing null bytes from graffiti.
|
||||
// Example: "GEabcdPRxxxxSushi\x00\x00\x00..." becomes "GEabcdPRxxxxSushi"
|
||||
graffitiTrimmed := bytes.TrimRight(graffitiInBlock[:], "\x00")
|
||||
for _, graffiti := range helpers.Graffiti {
|
||||
if bytes.Equal(bytesutil.PadTo([]byte(graffiti), 32), graffitiInBlock[:]) {
|
||||
e = true
|
||||
// Check suffix match since graffiti now has EL/CL version prefixes.
|
||||
// bytes.HasSuffix returns true if the first arg ends with the second arg.
|
||||
// Examples:
|
||||
// bytes.HasSuffix([]byte("GEabcdPRxxxxSushi"), []byte("Sushi")) → true
|
||||
// bytes.HasSuffix([]byte("PRxxxxRamen"), []byte("Ramen")) → true
|
||||
// bytes.HasSuffix([]byte("Sushi"), []byte("Sushi")) → true (exact match)
|
||||
// bytes.HasSuffix([]byte("SushiXXX"), []byte("Sushi")) → false (not at end)
|
||||
if bytes.HasSuffix(graffitiTrimmed, []byte(graffiti)) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !e && slot != 0 {
|
||||
return errors.New("could not get graffiti from the list")
|
||||
if !found && slot != 0 {
|
||||
return fmt.Errorf("block at slot %d has graffiti %q which does not end with any expected graffiti", slot, string(graffitiTrimmed))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user