Skip to content

Commit 0a66516

Browse files
committed
refactor(core/types): simplify Header RLP overriding
1 parent c74b645 commit 0a66516

File tree

2 files changed

+79
-64
lines changed

2 files changed

+79
-64
lines changed

core/types/block.libevm.go

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"io"
23+
"math/big"
2324

25+
"github.com/ava-labs/libevm/common"
2426
"github.com/ava-labs/libevm/libevm/pseudo"
2527
"github.com/ava-labs/libevm/libevm/testonly"
2628
"github.com/ava-labs/libevm/rlp"
@@ -31,8 +33,8 @@ import (
3133
type HeaderHooks interface {
3234
MarshalJSON(*Header) ([]byte, error) //nolint:govet // Type-specific override hook
3335
UnmarshalJSON(*Header, []byte) error //nolint:govet
34-
EncodeRLP(*Header, io.Writer) error
35-
DecodeRLP(*Header, *rlp.Stream) error
36+
RLPFieldsForEncoding(*Header) *rlp.Fields
37+
RLPFieldPointersForDecoding(*Header) *rlp.Fields
3638
PostCopy(dst *Header)
3739
}
3840

@@ -68,12 +70,12 @@ func (h *Header) UnmarshalJSON(b []byte) error {
6870

6971
// EncodeRLP implements the [rlp.Encoder] interface.
7072
func (h *Header) EncodeRLP(w io.Writer) error {
71-
return h.hooks().EncodeRLP(h, w)
73+
return h.hooks().RLPFieldsForEncoding(h).EncodeRLP(w)
7274
}
7375

7476
// DecodeRLP implements the [rlp.Decoder] interface.
7577
func (h *Header) DecodeRLP(s *rlp.Stream) error {
76-
return h.hooks().DecodeRLP(h, s)
78+
return h.hooks().RLPFieldPointersForDecoding(h).DecodeRLP(s)
7779
}
7880

7981
func (h *Header) extraPayload() *pseudo.Type {
@@ -102,14 +104,46 @@ func (*NOOPHeaderHooks) UnmarshalJSON(h *Header, b []byte) error { //nolint:gove
102104
return h.unmarshalJSON(b)
103105
}
104106

105-
func (*NOOPHeaderHooks) EncodeRLP(h *Header, w io.Writer) error {
106-
return h.encodeRLP(w)
107+
func init() {
108+
var (
109+
h common.Hash
110+
a common.Address
111+
bi *big.Int
112+
u uint64
113+
_ = Header{
114+
h, h, a, h, h, h, Bloom{}, bi, bi, u, u, u, []byte{}, h, BlockNonce{}, // required
115+
bi, &h, &u, &u, &h, //optional
116+
&pseudo.Type{}, // libevm
117+
}
118+
)
107119
}
108120

109-
func (*NOOPHeaderHooks) DecodeRLP(h *Header, s *rlp.Stream) error {
110-
type withoutMethods Header
111-
return s.Decode((*withoutMethods)(h))
121+
func (*NOOPHeaderHooks) RLPFieldsForEncoding(h *Header) *rlp.Fields {
122+
// TODO(arr4n): write a generator for this and the pointer equivalent, and
123+
// include [NOOPBodyHooks] when running it.
124+
return &rlp.Fields{
125+
Required: []any{
126+
h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom,
127+
h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce,
128+
},
129+
Optional: []any{
130+
h.BaseFee, h.WithdrawalsHash, h.BlobGasUsed, h.ExcessBlobGas, h.ParentBeaconRoot,
131+
},
132+
}
112133
}
134+
135+
func (*NOOPHeaderHooks) RLPFieldPointersForDecoding(h *Header) *rlp.Fields {
136+
return &rlp.Fields{
137+
Required: []any{
138+
&h.ParentHash, &h.UncleHash, &h.Coinbase, &h.Root, &h.TxHash, &h.ReceiptHash, &h.Bloom,
139+
&h.Difficulty, &h.Number, &h.GasLimit, &h.GasUsed, &h.Time, &h.Extra, &h.MixDigest, &h.Nonce,
140+
},
141+
Optional: []any{
142+
&h.BaseFee, &h.WithdrawalsHash, &h.BlobGasUsed, &h.ExcessBlobGas, &h.ParentBeaconRoot,
143+
},
144+
}
145+
}
146+
113147
func (*NOOPHeaderHooks) PostCopy(dst *Header) {}
114148

115149
var _ interface {

core/types/block.libevm_test.go

Lines changed: 36 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,62 +20,62 @@ import (
2020
"encoding/json"
2121
"errors"
2222
"fmt"
23-
"io"
2423
"testing"
2524

2625
"github.com/stretchr/testify/assert"
2726
"github.com/stretchr/testify/require"
2827

28+
"github.com/ava-labs/libevm/common"
2929
. "github.com/ava-labs/libevm/core/types"
30-
"github.com/ava-labs/libevm/crypto"
3130
"github.com/ava-labs/libevm/libevm/ethtest"
3231
"github.com/ava-labs/libevm/libevm/pseudo"
3332
"github.com/ava-labs/libevm/rlp"
3433
)
3534

3635
type stubHeaderHooks struct {
37-
suffix []byte
38-
gotRawJSONToUnmarshal, gotRawRLPToDecode []byte
39-
setHeaderToOnUnmarshalOrDecode Header
40-
accessor pseudo.Accessor[*Header, *stubHeaderHooks]
41-
toCopy *stubHeaderHooks
36+
suffix []byte
37+
gotRawJSONToUnmarshal []byte
38+
setHeaderToOnUnmarshal Header
39+
accessor pseudo.Accessor[*Header, *stubHeaderHooks]
40+
toCopy *stubHeaderHooks
4241

43-
errMarshal, errUnmarshal, errEncode, errDecode error
42+
errMarshal, errUnmarshal error
4443
}
4544

4645
func fakeHeaderJSON(h *Header, suffix []byte) []byte {
4746
return []byte(fmt.Sprintf(`"%#x:%#x"`, h.ParentHash, suffix))
4847
}
4948

50-
func fakeHeaderRLP(h *Header, suffix []byte) []byte {
51-
return append(crypto.Keccak256(h.ParentHash[:]), suffix...)
52-
}
53-
5449
func (hh *stubHeaderHooks) MarshalJSON(h *Header) ([]byte, error) { //nolint:govet
5550
return fakeHeaderJSON(h, hh.suffix), hh.errMarshal
5651
}
5752

5853
func (hh *stubHeaderHooks) UnmarshalJSON(h *Header, b []byte) error { //nolint:govet
5954
hh.gotRawJSONToUnmarshal = b
60-
*h = hh.setHeaderToOnUnmarshalOrDecode
55+
*h = hh.setHeaderToOnUnmarshal
6156
return hh.errUnmarshal
6257
}
6358

64-
func (hh *stubHeaderHooks) EncodeRLP(h *Header, w io.Writer) error {
65-
if _, err := w.Write(fakeHeaderRLP(h, hh.suffix)); err != nil {
66-
return err
67-
}
68-
return hh.errEncode
59+
func directEncodeHeaderRLP(tb testing.TB, h *Header, extraPayloadSuffix []byte) []byte {
60+
tb.Helper()
61+
62+
// The encoded type mirrors the fields returned by
63+
// [stubHeaderHooks.RLPFieldsForEncoding].
64+
buf, err := rlp.EncodeToBytes(struct {
65+
ParentHash common.Hash
66+
Suffix []byte
67+
}{h.ParentHash, extraPayloadSuffix})
68+
69+
require.NoError(tb, err)
70+
return buf
6971
}
7072

71-
func (hh *stubHeaderHooks) DecodeRLP(h *Header, s *rlp.Stream) error {
72-
r, err := s.Raw()
73-
if err != nil {
74-
return err
75-
}
76-
hh.gotRawRLPToDecode = r
77-
*h = hh.setHeaderToOnUnmarshalOrDecode
78-
return hh.errDecode
73+
func (hh *stubHeaderHooks) RLPFieldsForEncoding(h *Header) *rlp.Fields {
74+
return &rlp.Fields{Required: []any{h.ParentHash, hh.suffix}}
75+
}
76+
77+
func (hh *stubHeaderHooks) RLPFieldPointersForDecoding(h *Header) *rlp.Fields {
78+
return &rlp.Fields{Required: []any{&h.ParentHash, &hh.suffix}}
7979
}
8080

8181
func (hh *stubHeaderHooks) PostCopy(dst *Header) {
@@ -104,7 +104,7 @@ func TestHeaderHooks(t *testing.T) {
104104
t.Run("UnmarshalJSON", func(t *testing.T) {
105105
hdr := new(Header)
106106
stub := &stubHeaderHooks{
107-
setHeaderToOnUnmarshalOrDecode: Header{
107+
setHeaderToOnUnmarshal: Header{
108108
Extra: []byte("can you solve this puzzle? 0xbda01b6cf56c303bd3f581599c0d5c0b"),
109109
},
110110
}
@@ -115,31 +115,26 @@ func TestHeaderHooks(t *testing.T) {
115115
require.NoErrorf(t, err, "json.Unmarshal()")
116116

117117
assert.Equal(t, input, string(stub.gotRawJSONToUnmarshal), "raw JSON received by hook")
118-
assert.Equal(t, &stub.setHeaderToOnUnmarshalOrDecode, hdr, "%T after JSON unmarshalling with hook", hdr)
118+
assert.Equal(t, &stub.setHeaderToOnUnmarshal, hdr, "%T after JSON unmarshalling with hook", hdr)
119119
})
120120

121121
t.Run("EncodeRLP", func(t *testing.T) {
122122
got, err := rlp.EncodeToBytes(hdr)
123123
require.NoError(t, err, "rlp.EncodeToBytes(%T)", hdr)
124-
assert.Equal(t, fakeHeaderRLP(hdr, suffix), got)
124+
assert.Equal(t, directEncodeHeaderRLP(t, hdr, suffix), got)
125125
})
126126

127127
t.Run("DecodeRLP", func(t *testing.T) {
128-
input, err := rlp.EncodeToBytes(rng.Bytes(8))
129-
require.NoError(t, err)
128+
input := directEncodeHeaderRLP(t, hdr, suffix)
130129

131-
hdr := new(Header)
132-
stub := &stubHeaderHooks{
133-
setHeaderToOnUnmarshalOrDecode: Header{
134-
Extra: []byte("arr4n was here"),
135-
},
136-
}
137-
extras.Header.Set(hdr, stub)
138-
err = rlp.DecodeBytes(input, hdr)
130+
got := new(Header)
131+
stub := &stubHeaderHooks{}
132+
extras.Header.Set(got, stub)
133+
err := rlp.DecodeBytes(input, got)
139134
require.NoErrorf(t, err, "rlp.DecodeBytes(%#x)", input)
140135

141-
assert.Equal(t, input, stub.gotRawRLPToDecode, "raw RLP received by hooks")
142-
assert.Equalf(t, &stub.setHeaderToOnUnmarshalOrDecode, hdr, "%T after RLP decoding with hook", hdr)
136+
assert.Equalf(t, hdr.ParentHash, got.ParentHash, "RLP-decoded %T.ParentHash", hdr)
137+
assert.Equalf(t, suffix, stub.suffix, "RLP-decoded %T.suffix", stub)
143138
})
144139

145140
t.Run("PostCopy", func(t *testing.T) {
@@ -159,16 +154,12 @@ func TestHeaderHooks(t *testing.T) {
159154
t.Run("error_propagation", func(t *testing.T) {
160155
errMarshal := errors.New("whoops")
161156
errUnmarshal := errors.New("is it broken?")
162-
errEncode := errors.New("uh oh")
163-
errDecode := errors.New("something bad happened")
164157

165158
hdr := new(Header)
166159
setStub := func() {
167160
extras.Header.Set(hdr, &stubHeaderHooks{
168161
errMarshal: errMarshal,
169162
errUnmarshal: errUnmarshal,
170-
errEncode: errEncode,
171-
errDecode: errDecode,
172163
})
173164
}
174165

@@ -184,15 +175,5 @@ func TestHeaderHooks(t *testing.T) {
184175
err := json.Unmarshal([]byte("{}"), hdr)
185176
assert.Equal(t, errUnmarshal, err, "via json.Unmarshal()")
186177
}
187-
188-
setStub() // [stubHeaderHooks] completely overrides the Header
189-
{
190-
err := rlp.Encode(io.Discard, hdr)
191-
assert.Equal(t, errEncode, err, "via rlp.Encode()")
192-
}
193-
{
194-
err := rlp.DecodeBytes([]byte{0}, hdr)
195-
assert.Equal(t, errDecode, err, "via rlp.DecodeBytes()")
196-
}
197178
})
198179
}

0 commit comments

Comments
 (0)