Skip to content

Commit af1905c

Browse files
committed
feat: general-purpose block-header RLP modification
1 parent 3a491f9 commit af1905c

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

core/types/block.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
"github.com/ava-labs/libevm/common"
3030
"github.com/ava-labs/libevm/common/hexutil"
31+
"github.com/ava-labs/libevm/libevm/pseudo"
3132
"github.com/ava-labs/libevm/rlp"
3233
)
3334

@@ -93,6 +94,8 @@ type Header struct {
9394

9495
// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
9596
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
97+
98+
extra *pseudo.Type
9699
}
97100

98101
// field type overrides for gencodec

core/types/gen_header_rlp.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/rlp_backwards_compat.libevm_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,10 @@ func TestHeaderRLPBackwardsCompatibility(t *testing.T) {
112112
require.NoError(t, err)
113113
assert.Equal(t, wantRLP, got)
114114
})
115+
116+
t.Run("DecodeRLP", func(t *testing.T) {
117+
got := new(Header)
118+
require.NoError(t, rlp.DecodeBytes(wantRLP, got))
119+
assert.Equal(t, hdr, got)
120+
})
115121
}

core/types/rlp_payload.libevm.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
package types
1818

1919
import (
20+
"bytes"
2021
"fmt"
2122
"io"
2223

2324
"github.com/ava-labs/libevm/libevm/pseudo"
2425
"github.com/ava-labs/libevm/libevm/testonly"
2526
"github.com/ava-labs/libevm/rlp"
27+
"golang.org/x/sync/errgroup"
2628
)
2729

2830
// RegisterExtras registers the type `SA` to be carried as an extra payload in
@@ -234,3 +236,80 @@ func (e *StateAccountExtra) Format(s fmt.State, verb rune) {
234236
}
235237
_, _ = s.Write([]byte(out))
236238
}
239+
240+
func (h *Header) EncodeRLP(w io.Writer) error {
241+
// TODO(arr4n) if no extra payloads have been registered then return
242+
// h.genEncodeRLP(w) immediately.
243+
244+
var buf bytes.Buffer
245+
if err := h.genEncodeRLP(&buf); err != nil {
246+
return err
247+
}
248+
249+
tree, err := rlp.ParseTree(buf.Bytes())
250+
if err != nil {
251+
return err
252+
}
253+
list, ok := tree.(rlp.ListNode)
254+
if !ok {
255+
return fmt.Errorf("rlp.ParseTree(%T RLP) got %T; expecting %T", h, tree, list)
256+
}
257+
258+
// TODO(arr4n) call a list-modifying hook here.
259+
//
260+
// - Both coreth and subnet-evm remove WithdrawalsHash, which is list[16].
261+
// What they include is, however, different.
262+
//
263+
// - Special attention needs to be paid to the existence (or not) of
264+
// optional fields so raw slice indexing MUST NOT be performed.
265+
//
266+
// - The hook MUST be provided with the concrete type wrapped by
267+
// [Header.extra] and probably SHOULD receive the Header itself.
268+
//
269+
// - It is likely that the easiest way to implement specific hooks is for
270+
// them to RLP-encode the extra payload itself, parse its tree, and insert
271+
// the nodes where appropriate.
272+
273+
return list.EncodeRLP(w)
274+
}
275+
276+
func (h *Header) DecodeRLP(s *rlp.Stream) error {
277+
// TODO(arr4n) if no extra payloads have been registered then return
278+
// s.Decode(hh) immediately. See `hh` at the end of the method.
279+
280+
raw, err := s.Raw()
281+
if err != nil {
282+
return err
283+
}
284+
285+
tree, err := rlp.ParseTree(raw)
286+
if err != nil {
287+
return err
288+
}
289+
list, ok := tree.(rlp.ListNode)
290+
if !ok {
291+
return fmt.Errorf("rlp.ParseTree(%T RLP) got %T; expecting %T", h, tree, list)
292+
}
293+
294+
// TODO(arr4n) call a hook that inverts the modifier of [Header.EncodeRLP].
295+
296+
// It's not safe to reuse the `raw` slice's memory because that's what is
297+
// backing `list`, so we pipe directly from the tree encoder into the header
298+
// decoder.
299+
r, w := io.Pipe()
300+
defer r.Close()
301+
var g errgroup.Group
302+
g.Go(func() error {
303+
if err := list.EncodeRLP(w); err != nil {
304+
return err
305+
}
306+
return w.Close()
307+
})
308+
g.Go(func() error {
309+
// Avoid infinite recursion due to calling h.DecodeRLP() again.
310+
type noMethodsHdr Header
311+
hh := (*noMethodsHdr)(h)
312+
return rlp.NewStream(r, 0 /*no input limit*/).Decode(hh)
313+
})
314+
return g.Wait()
315+
}

0 commit comments

Comments
 (0)