diff --git a/cache/redis_cache.go b/cache/redis_cache.go index 310b2ae0..ed73da84 100644 --- a/cache/redis_cache.go +++ b/cache/redis_cache.go @@ -3,7 +3,6 @@ package cache import ( "context" "encoding/json" - "fmt" "strconv" "time" @@ -35,12 +34,12 @@ func InitRedisCache(ctx context.Context, redisAddress string, keyPrefix string) } func (cache *RedisCache) SetString(ctx context.Context, key, value string, expiration time.Duration) error { - return cache.redisRemoteCache.Set(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key), value, expiration).Err() + return cache.redisRemoteCache.Set(ctx, cache.keyPrefix+key, value, expiration).Err() } func (cache *RedisCache) GetString(ctx context.Context, key string) (string, error) { - value, err := cache.redisRemoteCache.Get(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key)).Result() + value, err := cache.redisRemoteCache.Get(ctx, cache.keyPrefix+key).Result() if err != nil { return "", err } @@ -49,12 +48,12 @@ func (cache *RedisCache) GetString(ctx context.Context, key string) (string, err } func (cache *RedisCache) SetUint64(ctx context.Context, key string, value uint64, expiration time.Duration) error { - return cache.redisRemoteCache.Set(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key), fmt.Sprintf("%d", value), expiration).Err() + return cache.redisRemoteCache.Set(ctx, cache.keyPrefix+key, strconv.FormatUint(value, 10), expiration).Err() } func (cache *RedisCache) GetUint64(ctx context.Context, key string) (uint64, error) { - value, err := cache.redisRemoteCache.Get(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key)).Result() + value, err := cache.redisRemoteCache.Get(ctx, cache.keyPrefix+key).Result() if err != nil { return 0, err } @@ -67,12 +66,12 @@ func (cache *RedisCache) GetUint64(ctx context.Context, key string) (uint64, err } func (cache *RedisCache) SetBool(ctx context.Context, key string, value bool, expiration time.Duration) error { - return cache.redisRemoteCache.Set(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key), fmt.Sprintf("%t", value), expiration).Err() + return cache.redisRemoteCache.Set(ctx, cache.keyPrefix+key, strconv.FormatBool(value), expiration).Err() } func (cache *RedisCache) GetBool(ctx context.Context, key string) (bool, error) { - value, err := cache.redisRemoteCache.Get(ctx, key).Result() + value, err := cache.redisRemoteCache.Get(ctx, cache.keyPrefix+key).Result() if err != nil { return false, err } @@ -85,11 +84,11 @@ func (cache *RedisCache) GetBool(ctx context.Context, key string) (bool, error) } func (cache *RedisCache) SetBytes(ctx context.Context, key string, value []byte, expiration time.Duration) error { - return cache.redisRemoteCache.Set(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key), value, expiration).Err() + return cache.redisRemoteCache.Set(ctx, cache.keyPrefix+key, value, expiration).Err() } func (cache *RedisCache) GetBytes(ctx context.Context, key string) ([]byte, error) { - value, err := cache.redisRemoteCache.Get(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key)).Result() + value, err := cache.redisRemoteCache.Get(ctx, cache.keyPrefix+key).Result() if err != nil { return nil, err } @@ -101,18 +100,18 @@ func (cache *RedisCache) Set(ctx context.Context, key string, value interface{}, if err != nil { return err } - return cache.redisRemoteCache.Set(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key), valueMarshal, expiration).Err() + return cache.redisRemoteCache.Set(ctx, cache.keyPrefix+key, valueMarshal, expiration).Err() } func (cache *RedisCache) Get(ctx context.Context, key string, returnValue interface{}) (interface{}, error) { - value, err := cache.redisRemoteCache.Get(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key)).Result() + value, err := cache.redisRemoteCache.Get(ctx, cache.keyPrefix+key).Result() if err != nil { return nil, err } err = json.Unmarshal([]byte(value), returnValue) if err != nil { - cache.redisRemoteCache.Del(ctx, fmt.Sprintf("%s%s", cache.keyPrefix, key)).Err() + cache.redisRemoteCache.Del(ctx, cache.keyPrefix+key).Err() utils.LogError(err, "error unmarshalling data for key", 0, map[string]interface{}{"key": key}) return nil, err } diff --git a/cache/tiered_cache.go b/cache/tiered_cache.go index 5d52f7c1..0dbef57d 100644 --- a/cache/tiered_cache.go +++ b/cache/tiered_cache.go @@ -79,6 +79,15 @@ func NewTieredCache(cacheSize int, redisAddress string, redisPrefix string, logg }, nil } +// pageKeyPrefix extracts the page type from a cache key by returning +// the portion before the first ':' separator (e.g. "slots" from "slots:123:456"). +func pageKeyPrefix(key string) string { + if before, _, ok := strings.Cut(key, ":"); ok { + return before + } + return key +} + func (cache *TieredCache) isSszCompatible(value interface{}, key string) bool { if utils.Config.KillSwitch.DisableSSZPageCache { return false @@ -96,13 +105,17 @@ func (cache *TieredCache) isSszCompatible(value interface{}, key string) bool { cache.sszCompatMutex.Lock() defer cache.sszCompatMutex.Unlock() + // Re-check after acquiring write lock to avoid redundant validation. + if compat, ok = cache.sszCompatMap[valType]; ok { + return compat + } + err := modelDynSsz.ValidateType(valType) compat = err == nil cache.sszCompatMap[valType] = compat if err != nil { - keySplit := strings.Split(key, ":") - cache.logger.WithError(err).Warnf("page model not ssz compatible: %v (%v)", keySplit[0], valType.Name()) + cache.logger.WithError(err).Warnf("page model not ssz compatible: %v (%v)", pageKeyPrefix(key), valType.Name()) } return compat @@ -166,8 +179,7 @@ func (cache *TieredCache) Set(key string, value interface{}, expiration time.Dur cache.sszCompatMutex.Lock() cache.sszCompatMap[cacheType] = false cache.sszCompatMutex.Unlock() - keySplit := strings.Split(key, ":") - cache.logger.WithError(err).Warnf("page model not ssz compatible: %v (%v): %v", keySplit[0], cacheType.Name(), err) + cache.logger.WithError(err).Warnf("page model not ssz compatible: %v (%v)", pageKeyPrefix(key), cacheType.Name()) return err } diff --git a/handlers/blocks.go b/handlers/blocks.go index 0fc8a2c5..e16c4c26 100644 --- a/handlers/blocks.go +++ b/handlers/blocks.go @@ -3,6 +3,7 @@ package handlers import ( "bytes" "context" + "encoding/hex" "fmt" "math" "net/http" @@ -286,7 +287,7 @@ func buildBlocksPageData(ctx context.Context, firstSlot uint64, pageSize uint64, } if pageData.DisplayMevBlock && dbSlot.EthBlockHash != nil { - if mevBlock, exists := mevBlocksMap[fmt.Sprintf("%x", dbSlot.EthBlockHash)]; exists { + if mevBlock, exists := mevBlocksMap[hex.EncodeToString(dbSlot.EthBlockHash)]; exists { slotData.IsMevBlock = true var relays []string @@ -375,11 +376,11 @@ func buildBlocksPageData(ctx context.Context, firstSlot uint64, pageSize uint64, } func getClientTypeName(clientType uint8) string { - if clientType > 0 { - return execution.ClientType(clientType).String() + if clientType == 0 { + return "Unknown(0)" } - return fmt.Sprintf("Unknown(%d)", clientType) + return execution.ClientType(clientType).String() } func buildBlocksPageSlotGraph(ctx context.Context, pageData *models.BlocksPageData, slotData *models.BlocksPageDataSlot, maxOpenFork *int, openForks map[int][]byte, isFirstPage bool) { diff --git a/handlers/blocks_filtered.go b/handlers/blocks_filtered.go index 5838dbd2..4c0e7842 100644 --- a/handlers/blocks_filtered.go +++ b/handlers/blocks_filtered.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "encoding/hex" "fmt" "net/http" "net/url" @@ -395,7 +396,7 @@ func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize u mevBlockRelays := "" if dbBlock.Block.EthBlockHash != nil { - if mevBlock, exists := mevBlocksMap[fmt.Sprintf("%x", dbBlock.Block.EthBlockHash)]; exists { + if mevBlock, exists := mevBlocksMap[hex.EncodeToString(dbBlock.Block.EthBlockHash)]; exists { isMevBlock = true var relays []string diff --git a/handlers/builder.go b/handlers/builder.go index 2eec185a..b356336e 100644 --- a/handlers/builder.go +++ b/handlers/builder.go @@ -1,6 +1,7 @@ package handlers import ( + "bytes" "context" "encoding/hex" "errors" @@ -48,7 +49,7 @@ func BuilderDetail(w http.ResponseWriter, r *http.Request) { var superseded bool vars := mux.Vars(r) - idxOrPubKey := strings.Replace(vars["idxOrPubKey"], "0x", "", -1) + idxOrPubKey := strings.TrimPrefix(vars["idxOrPubKey"], "0x") builderPubKey, err := hex.DecodeString(idxOrPubKey) if err != nil || len(builderPubKey) != 48 { // search by index @@ -384,7 +385,7 @@ func buildBuilderRecentBlocks(ctx context.Context, builderIndex uint64, chainSta copy(parentRoot[:], slot.ParentRoot) bids := indexer.GetBlockBids(parentRoot) for _, bid := range bids { - if bid.BuilderIndex == builderIndexInt64 && fmt.Sprintf("%x", bid.BlockHash) == fmt.Sprintf("%x", slot.EthBlockHash) { + if bid.BuilderIndex == builderIndexInt64 && bytes.Equal(bid.BlockHash, slot.EthBlockHash) { block.Value = bid.Value block.ElPayment = bid.ElPayment break @@ -407,7 +408,7 @@ func buildBuilderRecentBids(ctx context.Context, builderIndex uint64, chainState bidBlockHashes := make(map[string]bool, len(bids)) var minSlot, maxSlot uint64 for i, bid := range bids { - bidBlockHashes[fmt.Sprintf("%x", bid.BlockHash)] = true + bidBlockHashes[hex.EncodeToString(bid.BlockHash)] = true if i == 0 || bid.Slot > maxSlot { maxSlot = bid.Slot } @@ -431,7 +432,7 @@ func buildBuilderRecentBids(ctx context.Context, builderIndex uint64, chainState if assignedSlot.Block == nil { continue } - hashKey := fmt.Sprintf("%x", assignedSlot.Block.EthBlockHash) + hashKey := hex.EncodeToString(assignedSlot.Block.EthBlockHash) if bidBlockHashes[hashKey] && assignedSlot.Block.PayloadStatus == dbtypes.PayloadStatusCanonical { canonicalBlockHashes[hashKey] = true } @@ -449,7 +450,7 @@ func buildBuilderRecentBids(ctx context.Context, builderIndex uint64, chainState GasLimit: bid.GasLimit, Value: bid.Value, ElPayment: bid.ElPayment, - IsWinning: canonicalBlockHashes[fmt.Sprintf("%x", bid.BlockHash)], + IsWinning: canonicalBlockHashes[hex.EncodeToString(bid.BlockHash)], } result = append(result, bidData) diff --git a/handlers/builders.go b/handlers/builders.go index e2d3ce73..a55ba03a 100644 --- a/handlers/builders.go +++ b/handlers/builders.go @@ -128,7 +128,7 @@ func buildBuildersPageData(ctx context.Context, pageNumber uint64, pageSize uint if filterPubKey != "" { pageData.FilterPubKey = filterPubKey filterArgs.Add("f.pubkey", filterPubKey) - filterPubKeyVal, _ := hex.DecodeString(strings.Replace(filterPubKey, "0x", "", -1)) + filterPubKeyVal, _ := hex.DecodeString(strings.TrimPrefix(filterPubKey, "0x")) builderFilter.PubKey = filterPubKeyVal } if filterIndex != "" { @@ -141,7 +141,7 @@ func buildBuildersPageData(ctx context.Context, pageNumber uint64, pageSize uint if filterExecutionAddr != "" { pageData.FilterExecutionAddr = filterExecutionAddr filterArgs.Add("f.execution_addr", filterExecutionAddr) - filterExecutionAddrVal, _ := hex.DecodeString(strings.Replace(filterExecutionAddr, "0x", "", -1)) + filterExecutionAddrVal, _ := hex.DecodeString(strings.TrimPrefix(filterExecutionAddr, "0x")) builderFilter.ExecutionAddress = filterExecutionAddrVal } if filterStatus != "" { diff --git a/handlers/search.go b/handlers/search.go index f941084d..e97b45ee 100644 --- a/handlers/search.go +++ b/handlers/search.go @@ -105,8 +105,7 @@ func buildSearchResolverResult(ctx context.Context, searchQuery string) (searchR } } - hashQuery := strings.Replace(searchQuery, "0x", "", -1) - hashQuery = strings.Replace(hashQuery, "0X", "", -1) + hashQuery := strings.TrimPrefix(strings.TrimPrefix(searchQuery, "0x"), "0X") if len(hashQuery) == 96 { validatorPubkey, err := hex.DecodeString(hashQuery) if err == nil && len(validatorPubkey) == 48 { @@ -199,8 +198,7 @@ func SearchAhead(w http.ResponseWriter, r *http.Request) { searchType := vars["type"] urlArgs := r.URL.Query() search := strings.Trim(urlArgs.Get("q"), " \t") - search = strings.Replace(search, "0x", "", -1) - search = strings.Replace(search, "0X", "", -1) + search = strings.TrimPrefix(strings.TrimPrefix(search, "0x"), "0X") // 404 before cache so we don't cache disabled/unknown types allowedTypes := map[string]bool{ diff --git a/handlers/slot.go b/handlers/slot.go index dbc7033b..f9f840ae 100644 --- a/handlers/slot.go +++ b/handlers/slot.go @@ -61,7 +61,7 @@ func Slot(w http.ResponseWriter, r *http.Request) { ) vars := mux.Vars(r) - slotOrHash := strings.Replace(vars["slotOrHash"], "0x", "", -1) + slotOrHash := strings.TrimPrefix(vars["slotOrHash"], "0x") blockSlot := int64(-1) blockRootHash, err := hex.DecodeString(slotOrHash) if err != nil || len(slotOrHash) != 64 { @@ -144,7 +144,7 @@ func SlotBlob(w http.ResponseWriter, r *http.Request) { return } - blockRoot, err := hex.DecodeString(strings.Replace(vars["root"], "0x", "", -1)) + blockRoot, err := hex.DecodeString(strings.TrimPrefix(vars["root"], "0x")) if err != nil || len(blockRoot) != 32 { http.Error(w, "Invalid block root", http.StatusBadRequest) return diff --git a/handlers/slots.go b/handlers/slots.go index 155d2745..95075174 100644 --- a/handlers/slots.go +++ b/handlers/slots.go @@ -3,6 +3,7 @@ package handlers import ( "bytes" "context" + "encoding/hex" "fmt" "math" "net/http" @@ -295,7 +296,7 @@ func buildSlotsPageData(ctx context.Context, firstSlot uint64, pageSize uint64, } if pageData.DisplayMevBlock && dbSlot.EthBlockHash != nil { - if mevBlock, exists := mevBlocksMap[fmt.Sprintf("%x", dbSlot.EthBlockHash)]; exists { + if mevBlock, exists := mevBlocksMap[hex.EncodeToString(dbSlot.EthBlockHash)]; exists { slotData.IsMevBlock = true var relays []string diff --git a/handlers/slots_filtered.go b/handlers/slots_filtered.go index a466c753..963ba2ca 100644 --- a/handlers/slots_filtered.go +++ b/handlers/slots_filtered.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "encoding/hex" "fmt" "math" "net/http" @@ -544,7 +545,7 @@ func buildFilteredSlotsPageData(ctx context.Context, pageIdx uint64, pageSize ui slotData.PayloadStatus = uint8(payloadStatus) if pageData.DisplayMevBlock && dbBlock.Block.EthBlockHash != nil { - if mevBlock, exists := mevBlocksMap[fmt.Sprintf("%x", dbBlock.Block.EthBlockHash)]; exists { + if mevBlock, exists := mevBlocksMap[hex.EncodeToString(dbBlock.Block.EthBlockHash)]; exists { slotData.IsMevBlock = true var relays []string diff --git a/handlers/validator.go b/handlers/validator.go index 0f0999c7..b27e5deb 100644 --- a/handlers/validator.go +++ b/handlers/validator.go @@ -48,7 +48,7 @@ func Validator(w http.ResponseWriter, r *http.Request) { var validator *v1.Validator vars := mux.Vars(r) - idxOrPubKey := strings.Replace(vars["idxOrPubKey"], "0x", "", -1) + idxOrPubKey := strings.TrimPrefix(vars["idxOrPubKey"], "0x") validatorPubKey, err := hex.DecodeString(idxOrPubKey) if err != nil || len(validatorPubKey) != 48 { // search by index^ diff --git a/handlers/validators.go b/handlers/validators.go index c9c5c9e5..acb28b1e 100644 --- a/handlers/validators.go +++ b/handlers/validators.go @@ -129,7 +129,7 @@ func buildValidatorsPageData(ctx context.Context, pageNumber uint64, pageSize ui if filterPubKey != "" { pageData.FilterPubKey = filterPubKey filterArgs.Add("f.pubkey", filterPubKey) - filterPubKeyVal, _ := hex.DecodeString(strings.Replace(filterPubKey, "0x", "", -1)) + filterPubKeyVal, _ := hex.DecodeString(strings.TrimPrefix(filterPubKey, "0x")) validatorFilter.PubKey = filterPubKeyVal } if filterIndex != "" {