Allow hex strings in /eth/v1/beacon/states/{state_id}/root endpoint (#15098)

* Allow hex strings in `/eth/v1/beacon/states/{state_id}/root` endpoint

* changelog <3

* remove redundant conversion

* use `bytesutil.IsHex`
This commit is contained in:
Radosław Kapka
2025-03-27 15:13:57 +01:00
committed by GitHub
parent a3e61275a3
commit e42611ec72
5 changed files with 49 additions and 22 deletions

View File

@@ -53,7 +53,7 @@ func IsOptimistic(
}
return optimisticModeFetcher.IsOptimisticForRoot(ctx, bytesutil.ToBytes32(jcp.Root))
default:
if len(stateIdString) >= 2 && stateIdString[:2] == "0x" {
if bytesutil.IsHex(stateId) {
id, err := hexutil.Decode(stateIdString)
if err != nil {
return false, err

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
@@ -82,8 +81,7 @@ func (p *BeaconDbBlocker) Block(ctx context.Context, id []byte) (interfaces.Read
return nil, errors.Wrap(err, "could not retrieve genesis block")
}
default:
stringId := strings.ToLower(string(id))
if len(stringId) >= 2 && stringId[:2] == "0x" {
if bytesutil.IsHex(id) {
decoded, err := hexutil.Decode(string(id))
if err != nil {
e := NewBlockIdParseError(err)

View File

@@ -143,7 +143,7 @@ func (p *BeaconDbStater) State(ctx context.Context, stateId []byte) (state.Beaco
return nil, errors.Wrap(err, "could not get justified state")
}
default:
if len(stateIdString) >= 2 && stateIdString[:2] == "0x" {
if bytesutil.IsHex(stateId) {
decoded, parseErr := hexutil.Decode(string(stateId))
if parseErr != nil {
e := NewStateIdParseError(parseErr)
@@ -185,7 +185,15 @@ func (p *BeaconDbStater) StateRoot(ctx context.Context, stateId []byte) (root []
case "justified":
root, err = p.justifiedStateRoot(ctx)
default:
if len(stateId) == 32 {
if bytesutil.IsHex(stateId) {
var decoded []byte
decoded, err = hexutil.Decode(string(stateId))
if err != nil {
e := NewStateIdParseError(err)
return nil, &e
}
root, err = p.stateRootByRoot(ctx, decoded)
} else if len(stateId) == 32 {
root, err = p.stateRootByRoot(ctx, stateId)
} else {
slotNumber, parseErr := strconv.ParseUint(stateIdString, 10, 64)

View File

@@ -241,7 +241,6 @@ func TestGetStateRoot(t *testing.T) {
require.NoError(t, err)
assert.DeepEqual(t, stateRoot[:], s)
})
t.Run("genesis", func(t *testing.T) {
db := testDB.SetupDB(t)
b := util.NewBeaconBlock()
@@ -270,7 +269,6 @@ func TestGetStateRoot(t *testing.T) {
sr := genesisBlock.Block().StateRoot()
assert.DeepEqual(t, sr[:], s)
})
t.Run("finalized", func(t *testing.T) {
db := testDB.SetupDB(t)
genesis := bytesutil.ToBytes32([]byte("genesis"))
@@ -301,7 +299,6 @@ func TestGetStateRoot(t *testing.T) {
require.NoError(t, err)
assert.DeepEqual(t, blk.Block.StateRoot, s)
})
t.Run("justified", func(t *testing.T) {
db := testDB.SetupDB(t)
genesis := bytesutil.ToBytes32([]byte("genesis"))
@@ -332,30 +329,52 @@ func TestGetStateRoot(t *testing.T) {
require.NoError(t, err)
assert.DeepEqual(t, blk.Block.StateRoot, s)
})
t.Run("hex_root", func(t *testing.T) {
stateId, err := hexutil.Decode("0x" + strings.Repeat("0", 63) + "1")
require.NoError(t, err)
t.Run("hex", func(t *testing.T) {
hex := "0x" + strings.Repeat("0", 63) + "1"
p := BeaconDbStater{
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
s, err := p.StateRoot(ctx, stateId)
s, err := p.StateRoot(ctx, []byte(hex))
require.NoError(t, err)
assert.DeepEqual(t, stateId, s)
expected, err := hexutil.Decode(hex)
require.NoError(t, err)
assert.DeepEqual(t, expected, s)
})
t.Run("hex not found", func(t *testing.T) {
hex := "0x" + strings.Repeat("f", 64)
t.Run("hex_root_not_found", func(t *testing.T) {
p := BeaconDbStater{
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
stateId, err := hexutil.Decode("0x" + strings.Repeat("f", 64))
require.NoError(t, err)
_, err = p.StateRoot(ctx, stateId)
_, err = p.StateRoot(ctx, []byte(hex))
require.ErrorContains(t, "state root not found in the last 8192 state roots", err)
})
t.Run("bytes", func(t *testing.T) {
root, err := hexutil.Decode("0x" + strings.Repeat("0", 63) + "1")
require.NoError(t, err)
p := BeaconDbStater{
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
s, err := p.StateRoot(ctx, root)
require.NoError(t, err)
assert.DeepEqual(t, root, s)
})
t.Run("bytes not found", func(t *testing.T) {
root, err := hexutil.Decode("0x" + strings.Repeat("f", 64))
require.NoError(t, err)
p := BeaconDbStater{
ChainInfoFetcher: &chainMock.ChainService{State: newBeaconState},
}
_, err = p.StateRoot(ctx, root)
require.ErrorContains(t, "state root not found in the last 8192 state roots", err)
})
t.Run("slot", func(t *testing.T) {
db := testDB.SetupDB(t)
genesis := bytesutil.ToBytes32([]byte("genesis"))
@@ -382,8 +401,7 @@ func TestGetStateRoot(t *testing.T) {
require.NoError(t, err)
assert.DeepEqual(t, blk.Block.StateRoot, s)
})
t.Run("slot_too_big", func(t *testing.T) {
t.Run("slot too big", func(t *testing.T) {
p := BeaconDbStater{
GenesisTimeFetcher: &chainMock.ChainService{
Genesis: time.Now(),
@@ -393,7 +411,7 @@ func TestGetStateRoot(t *testing.T) {
assert.ErrorContains(t, "slot cannot be in the future", err)
})
t.Run("invalid_state", func(t *testing.T) {
t.Run("invalid state", func(t *testing.T) {
p := BeaconDbStater{}
_, err := p.StateRoot(ctx, []byte("foo"))
require.ErrorContains(t, "could not parse state ID", err)

View File

@@ -0,0 +1,3 @@
#### Ignored
- Allow hex strings in `/eth/v1/beacon/states/{state_id}/root` endpoint.