Skip to content

Commit 77dc1ac

Browse files
authored
internal/era: random access to header and receipts (#31544)
Co-authored-by: lightclient <[email protected]> Add GetHeaderByNumber and GetReceiptsByNumber to allow more efficient API request filling from Era files.
1 parent ff365af commit 77dc1ac

File tree

3 files changed

+112
-17
lines changed

3 files changed

+112
-17
lines changed

internal/era/e2store/e2store.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,15 @@ func (r *Reader) FindAll(want uint16) ([]*Entry, error) {
219219
off += int64(headerSize + length)
220220
}
221221
}
222+
223+
// SkipN skips `n` entries starting from `offset` and returns the new offset.
224+
func (r *Reader) SkipN(offset int64, n uint64) (int64, error) {
225+
for i := uint64(0); i < n; i++ {
226+
length, err := r.LengthAt(offset)
227+
if err != nil {
228+
return 0, err
229+
}
230+
offset += length
231+
}
232+
return offset, nil
233+
}

internal/era/era.go

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func ReadDir(dir, network string) ([]string, error) {
7070
}
7171
parts := strings.Split(entry.Name(), "-")
7272
if len(parts) != 3 || parts[0] != network {
73-
// invalid era1 filename, skip
73+
// Invalid era1 filename, skip.
7474
continue
7575
}
7676
if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
@@ -126,6 +126,29 @@ func (e *Era) Close() error {
126126
return e.f.Close()
127127
}
128128

129+
// GetHeaderByNumber returns the header for the given block number.
130+
func (e *Era) GetHeaderByNumber(num uint64) (*types.Header, error) {
131+
if e.m.start > num || e.m.start+e.m.count <= num {
132+
return nil, errors.New("out-of-bounds")
133+
}
134+
off, err := e.readOffset(num)
135+
if err != nil {
136+
return nil, err
137+
}
138+
139+
// Read and decompress header.
140+
r, _, err := newSnappyReader(e.s, TypeCompressedHeader, off)
141+
if err != nil {
142+
return nil, err
143+
}
144+
var header types.Header
145+
if err := rlp.Decode(r, &header); err != nil {
146+
return nil, err
147+
}
148+
return &header, nil
149+
}
150+
151+
// GetBlockByNumber returns the block for the given block number.
129152
func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
130153
if e.m.start > num || e.m.start+e.m.count <= num {
131154
return nil, errors.New("out-of-bounds")
@@ -154,6 +177,34 @@ func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
154177
return types.NewBlockWithHeader(&header).WithBody(body), nil
155178
}
156179

180+
// GetReceiptsByNumber returns the receipts for the given block number.
181+
func (e *Era) GetReceiptsByNumber(num uint64) (types.Receipts, error) {
182+
if e.m.start > num || e.m.start+e.m.count <= num {
183+
return nil, errors.New("out-of-bounds")
184+
}
185+
off, err := e.readOffset(num)
186+
if err != nil {
187+
return nil, err
188+
}
189+
190+
// Skip over header and body.
191+
off, err = e.s.SkipN(off, 2)
192+
if err != nil {
193+
return nil, err
194+
}
195+
196+
// Read and decompress receipts.
197+
r, _, err := newSnappyReader(e.s, TypeCompressedReceipts, off)
198+
if err != nil {
199+
return nil, err
200+
}
201+
var receipts types.Receipts
202+
if err := rlp.Decode(r, &receipts); err != nil {
203+
return nil, err
204+
}
205+
return receipts, nil
206+
}
207+
157208
// Accumulator reads the accumulator entry in the Era1 file.
158209
func (e *Era) Accumulator() (common.Hash, error) {
159210
entry, err := e.s.Find(TypeAccumulator)
@@ -187,13 +238,10 @@ func (e *Era) InitialTD() (*big.Int, error) {
187238
}
188239
off += n
189240

190-
// Skip over next two records.
191-
for i := 0; i < 2; i++ {
192-
length, err := e.s.LengthAt(off)
193-
if err != nil {
194-
return nil, err
195-
}
196-
off += length
241+
// Skip over header and body.
242+
off, err = e.s.SkipN(off, 2)
243+
if err != nil {
244+
return nil, err
197245
}
198246

199247
// Read total difficulty after first block.

internal/era/era_test.go

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ package era
1818

1919
import (
2020
"bytes"
21+
"fmt"
2122
"io"
2223
"math/big"
2324
"os"
2425
"testing"
2526

2627
"github.com/ethereum/go-ethereum/common"
28+
"github.com/ethereum/go-ethereum/core/types"
29+
"github.com/ethereum/go-ethereum/rlp"
2730
)
2831

2932
type testchain struct {
@@ -48,9 +51,9 @@ func TestEra1Builder(t *testing.T) {
4851
chain = testchain{}
4952
)
5053
for i := 0; i < 128; i++ {
51-
chain.headers = append(chain.headers, []byte{byte('h'), byte(i)})
52-
chain.bodies = append(chain.bodies, []byte{byte('b'), byte(i)})
53-
chain.receipts = append(chain.receipts, []byte{byte('r'), byte(i)})
54+
chain.headers = append(chain.headers, mustEncode(&types.Header{Number: big.NewInt(int64(i))}))
55+
chain.bodies = append(chain.bodies, mustEncode(&types.Body{Transactions: []*types.Transaction{types.NewTransaction(0, common.Address{byte(i)}, nil, 0, nil, nil)}}))
56+
chain.receipts = append(chain.receipts, mustEncode(&types.Receipts{{CumulativeGasUsed: uint64(i)}}))
5457
chain.tds = append(chain.tds, big.NewInt(int64(i)))
5558
}
5659

@@ -91,13 +94,25 @@ func TestEra1Builder(t *testing.T) {
9194
t.Fatalf("unexpected error %v", it.Error())
9295
}
9396
// Check headers.
94-
header, err := io.ReadAll(it.Header)
97+
rawHeader, err := io.ReadAll(it.Header)
98+
if err != nil {
99+
t.Fatalf("error reading header from iterator: %v", err)
100+
}
101+
if !bytes.Equal(rawHeader, chain.headers[i]) {
102+
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], rawHeader)
103+
}
104+
header, err := e.GetHeaderByNumber(i)
95105
if err != nil {
96106
t.Fatalf("error reading header: %v", err)
97107
}
98-
if !bytes.Equal(header, chain.headers[i]) {
99-
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], header)
108+
encHeader, err := rlp.EncodeToBytes(header)
109+
if err != nil {
110+
t.Fatalf("error encoding header: %v", err)
111+
}
112+
if !bytes.Equal(encHeader, chain.headers[i]) {
113+
t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], encHeader)
100114
}
115+
101116
// Check bodies.
102117
body, err := io.ReadAll(it.Body)
103118
if err != nil {
@@ -106,13 +121,25 @@ func TestEra1Builder(t *testing.T) {
106121
if !bytes.Equal(body, chain.bodies[i]) {
107122
t.Fatalf("mismatched body: want %s, got %s", chain.bodies[i], body)
108123
}
124+
109125
// Check receipts.
110-
receipts, err := io.ReadAll(it.Receipts)
126+
rawReceipts, err := io.ReadAll(it.Receipts)
127+
if err != nil {
128+
t.Fatalf("error reading receipts from iterator: %v", err)
129+
}
130+
if !bytes.Equal(rawReceipts, chain.receipts[i]) {
131+
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], rawReceipts)
132+
}
133+
receipts, err := e.GetReceiptsByNumber(i)
111134
if err != nil {
112135
t.Fatalf("error reading receipts: %v", err)
113136
}
114-
if !bytes.Equal(receipts, chain.receipts[i]) {
115-
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], receipts)
137+
encReceipts, err := rlp.EncodeToBytes(receipts)
138+
if err != nil {
139+
t.Fatalf("error encoding receipts: %v", err)
140+
}
141+
if !bytes.Equal(encReceipts, chain.receipts[i]) {
142+
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], encReceipts)
116143
}
117144

118145
// Check total difficulty.
@@ -144,3 +171,11 @@ func TestEraFilename(t *testing.T) {
144171
}
145172
}
146173
}
174+
175+
func mustEncode(obj any) []byte {
176+
b, err := rlp.EncodeToBytes(obj)
177+
if err != nil {
178+
panic(fmt.Sprintf("failed in encode obj: %v", err))
179+
}
180+
return b
181+
}

0 commit comments

Comments
 (0)