eth/protocols/eth: fix blockAccessList empty marker (#35286)

This PR addresses an issue in the eth71 `BlockAccessListsMsg` handler,

specifically: 
- if the requested bal is not accessible in the server side, 0x80
(EmptyString) will be returned as the marker
- at the client side, old message definition
`rlp.RawList[RawBlockAccessList]` assumes all the elements are List
- the message with 0x80 (kind = string) won't be decoded correctly
- the peer will be disconnected

The message definition has been changed to `rlp.RawList[rlp.RawValue]`,
which is aligned with the one in SNAP/2 protocol.
This commit is contained in:
rjl493456442 2026-07-03 17:15:07 +08:00 committed by GitHub
parent aa3d286f54
commit 3006c4411b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 51 additions and 12 deletions

View file

@ -742,7 +742,7 @@ func testGetBlockAccessLists(t *testing.T, protocol uint) {
var (
hashes []common.Hash
expect rlp.RawList[RawBlockAccessList]
expect rlp.RawList[rlp.RawValue]
)
for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ {
block := backend.chain.GetBlockByNumber(i)
@ -771,6 +771,42 @@ func testGetBlockAccessLists(t *testing.T, protocol uint) {
}
}
// TestBlockAccessListsUnavailableDecode checks that a BlockAccessLists response
// containing the EIP-8159 unavailability marker (RLP empty string).
func TestBlockAccessListsUnavailableDecode(t *testing.T) {
t.Parallel()
balRaw := makeTestBAL(t, common.Address{0x11})
// Assemble a response the way the serving side does, with the middle
// entry signaled as unavailable.
var list rlp.RawList[rlp.RawValue]
list.AppendRaw(balRaw)
list.AppendRaw(rlp.EmptyString)
list.AppendRaw(balRaw)
enc, err := rlp.EncodeToBytes(&BlockAccessListPacket{RequestId: 42, List: list})
if err != nil {
t.Fatalf("failed to encode packet: %v", err)
}
var packet BlockAccessListPacket
if err := rlp.DecodeBytes(enc, &packet); err != nil {
t.Fatalf("failed to decode packet: %v", err)
}
bals, err := packet.List.Items()
if err != nil {
t.Fatalf("failed to decode BAL entries: %v", err)
}
if len(bals) != 3 {
t.Fatalf("wrong entry count: got %d, want 3", len(bals))
}
for i, want := range [][]byte{balRaw, rlp.EmptyString, balRaw} {
if !bytes.Equal(bals[i], want) {
t.Errorf("entry %d mismatch: got %x, want %x", i, bals[i], want)
}
}
}
type decoder struct {
msg []byte
}

View file

@ -679,10 +679,10 @@ func handleGetBlockAccessLists(backend Backend, msg Decoder, peer *Peer) error {
// serviceGetBlockAccessListsQuery assembles the response to a BAL query.
// Unavailable BALs are returned as empty list entries.
func serviceGetBlockAccessListsQuery(chain *core.BlockChain, query GetBlockAccessListsRequest) rlp.RawList[RawBlockAccessList] {
func serviceGetBlockAccessListsQuery(chain *core.BlockChain, query GetBlockAccessListsRequest) rlp.RawList[rlp.RawValue] {
var (
bytes int
bals rlp.RawList[RawBlockAccessList]
bals rlp.RawList[rlp.RawValue]
)
for _, hash := range query {
if bytes >= softResponseLimit || bals.Len() >= maxBALsServe {
@ -720,7 +720,12 @@ func handleBlockAccessLists(backend Backend, msg Decoder, peer *Peer) error {
metadata := func() interface{} {
hashes := make([]common.Hash, len(bals))
for i := range bals {
hashes[i] = crypto.Keccak256Hash(bals[i].Bytes())
// Unavailable BALs (signaled by the empty string) are marked
// with the zero hash
if bytes.Equal(bals[i], rlp.EmptyString) {
continue
}
hashes[i] = crypto.Keccak256Hash(bals[i])
}
return hashes
}

View file

@ -258,7 +258,7 @@ func (p *Peer) ReplyReceiptsRLP70(id uint64, receipts rlp.RawList[*ReceiptList],
}
// ReplyBlockAccessLists is the response to GetBlockAccessLists (EIP-8159).
func (p *Peer) ReplyBlockAccessLists(id uint64, list rlp.RawList[RawBlockAccessList]) error {
func (p *Peer) ReplyBlockAccessLists(id uint64, list rlp.RawList[rlp.RawValue]) error {
return p2p.Send(p.rw, BlockAccessListsMsg, &BlockAccessListPacket{
RequestId: id,
List: list,

View file

@ -24,7 +24,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/bal"
"github.com/ethereum/go-ethereum/rlp"
)
@ -299,15 +298,14 @@ type GetBlockAccessListsPacket struct {
GetBlockAccessListsRequest
}
type RawBlockAccessList struct {
rlp.RawList[bal.AccountAccess]
}
type BlockAccessListResponse []RawBlockAccessList
// BlockAccessListResponse holds one raw entry per requested hash. Entries are
// kept as raw values because, per EIP-8159, the RLP empty string signals an
// unavailable BAL (an empty list is itself a valid BAL).
type BlockAccessListResponse []rlp.RawValue
type BlockAccessListPacket struct {
RequestId uint64
List rlp.RawList[RawBlockAccessList]
List rlp.RawList[rlp.RawValue]
}
func (*StatusPacket) Name() string { return "Status" }