Skip to content

Commit e27ce12

Browse files
committed
feat(core/types): BodyHooks for RLP overrides
1 parent 7f6afca commit e27ce12

9 files changed

+147
-19
lines changed

core/state/state.libevm_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func TestGetSetExtra(t *testing.T) {
4848
payloads := types.RegisterExtras[
4949
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
5050
types.NOOPBodyHooks, *types.NOOPBodyHooks,
51+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
5152
*accountExtra]().StateAccount
5253

5354
rng := ethtest.NewPseudoRand(42)

core/state/state_object.libevm_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ func TestStateObjectEmpty(t *testing.T) {
4949
types.RegisterExtras[
5050
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
5151
types.NOOPBodyHooks, *types.NOOPBodyHooks,
52+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
5253
bool]().StateAccount.Set(acc, false)
5354
types.RegisterExtras[
5455
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
5556
types.NOOPBodyHooks, *types.NOOPBodyHooks,
57+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
5658
bool]().StateAccount.Set(acc, false)
5759
},
5860
wantEmpty: true,
@@ -63,6 +65,7 @@ func TestStateObjectEmpty(t *testing.T) {
6365
types.RegisterExtras[
6466
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
6567
types.NOOPBodyHooks, *types.NOOPBodyHooks,
68+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
6669
bool]()
6770
},
6871
wantEmpty: true,
@@ -73,6 +76,7 @@ func TestStateObjectEmpty(t *testing.T) {
7376
types.RegisterExtras[
7477
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
7578
types.NOOPBodyHooks, *types.NOOPBodyHooks,
79+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
7680
bool]().StateAccount.Set(acc, true)
7781
},
7882
wantEmpty: false,

core/types/block.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ type Block struct {
211211
// inter-peer block relay.
212212
ReceivedAt time.Time
213213
ReceivedFrom interface{}
214+
215+
extra *pseudo.Type // See [RegisterExtras]
214216
}
215217

216218
// "external" block encoding. used for eth protocol, etc.
@@ -315,8 +317,8 @@ func CopyEthHeader(h *Header) *Header {
315317
return &cpy
316318
}
317319

318-
// DecodeRLP decodes a block from RLP.
319-
func (b *Block) DecodeRLP(s *rlp.Stream) error {
320+
// decodeRLP decodes a block from RLP.
321+
func (b *Block) decodeRLP(s *rlp.Stream) error {
320322
var eb extblock
321323
_, size, _ := s.Kind()
322324
if err := s.Decode(&eb); err != nil {
@@ -327,8 +329,8 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
327329
return nil
328330
}
329331

330-
// EncodeRLP serializes a block as RLP.
331-
func (b *Block) EncodeRLP(w io.Writer) error {
332+
// encodeRLP serializes a block as RLP.
333+
func (b *Block) encodeRLP(w io.Writer) error {
332334
return rlp.Encode(w, &extblock{
333335
Header: b.header,
334336
Txs: b.transactions,

core/types/block.libevm.go

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (h *Header) hooks() HeaderHooks {
4444
return new(NOOPHeaderHooks)
4545
}
4646

47-
func (e ExtraPayloads[HPtr, BodyExtraPtr, SA]) hooksFromHeader(h *Header) HeaderHooks {
47+
func (e ExtraPayloads[HPtr, BodyExtraPtr, BlockExtraPtr, SA]) hooksFromHeader(h *Header) HeaderHooks {
4848
return e.Header.Get(h)
4949
}
5050

@@ -135,7 +135,7 @@ func (b *Body) hooks() BodyHooks {
135135
return new(NOOPBodyHooks)
136136
}
137137

138-
func (e ExtraPayloads[HPtr, BodyExtraPtr, SA]) hooksFromBody(b *Body) BodyHooks {
138+
func (e ExtraPayloads[HPtr, BodyExtraPtr, BlockExtraPtr, SA]) hooksFromBody(b *Body) BodyHooks {
139139
return e.Body.Get(b)
140140
}
141141

@@ -181,3 +181,76 @@ func (*NOOPBodyHooks) DecodeRLP(b *Body, s *rlp.Stream) error {
181181
type withoutMethods Body
182182
return s.Decode((*withoutMethods)(b))
183183
}
184+
185+
// BlockHooks are required for all types registered with [RegisterExtras] for
186+
// [Block] payloads.
187+
type BlockHooks interface {
188+
EncodeRLP(*Block, io.Writer) error
189+
DecodeRLP(*Block, *rlp.Stream) error
190+
}
191+
192+
// hooks returns the Block's registered BlockHooks, if any, otherwise a
193+
// [*NOOPBlockHooks] suitable for running default behaviour.
194+
func (b *Block) hooks() BlockHooks {
195+
if r := registeredExtras; r.Registered() {
196+
return r.Get().hooks.hooksFromBlock(b)
197+
}
198+
return new(NOOPBlockHooks)
199+
}
200+
201+
func (e ExtraPayloads[HPtr, BodyExtraPtr, BlockExtraPtr, SA]) hooksFromBlock(b *Block) BlockHooks {
202+
return e.Block.Get(b)
203+
}
204+
205+
var _ interface {
206+
rlp.Encoder
207+
rlp.Decoder
208+
} = (*Block)(nil)
209+
210+
// EncodeRLP implements the [rlp.Encoder] interface.
211+
func (b *Block) EncodeRLP(w io.Writer) error {
212+
return b.hooks().EncodeRLP(b, w)
213+
}
214+
215+
// DecodeRLP implements the [rlp.Decoder] interface.
216+
func (b *Block) DecodeRLP(s *rlp.Stream) error {
217+
return b.hooks().DecodeRLP(b, s)
218+
}
219+
220+
func (b *Block) extraPayload() *pseudo.Type {
221+
r := registeredExtras
222+
if !r.Registered() {
223+
// See params.ChainConfig.extraPayload() for panic rationale.
224+
panic(fmt.Sprintf("%T.extraPayload() called before RegisterExtras()", r))
225+
}
226+
if b.extra == nil {
227+
b.extra = r.Get().newBlock()
228+
}
229+
return b.extra
230+
}
231+
232+
// NOOPBlockHooks implements [BlockHooks] such that they are equivalent to
233+
// no type having been registered.
234+
type NOOPBlockHooks struct{}
235+
236+
var _ BlockHooks = (*NOOPBlockHooks)(nil)
237+
238+
func (*NOOPBlockHooks) EncodeRLP(b *Block, w io.Writer) error {
239+
return b.encodeRLP(w)
240+
}
241+
242+
func (*NOOPBlockHooks) DecodeRLP(b *Block, s *rlp.Stream) error {
243+
return b.decodeRLP(s)
244+
}
245+
246+
func (b *Block) SetHeader(header *Header) {
247+
b.header = header
248+
}
249+
250+
func (b *Block) SetUncles(uncles []*Header) {
251+
b.uncles = uncles
252+
}
253+
254+
func (b *Block) SetTransactions(transactions Transactions) {
255+
b.transactions = transactions
256+
}

core/types/block.libevm_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,43 @@ func (bh *stubBodyHooks) DecodeRLP(b *Body, s *rlp.Stream) error {
104104
return bh.errDecode
105105
}
106106

107+
type stubBlockHooks struct {
108+
suffix []byte
109+
gotRawRLPToDecode []byte
110+
setBlockToOnUnmarshalOrDecode Block
111+
112+
errEncode, errDecode error
113+
}
114+
115+
func fakeBlockRLP(b *Block, suffix []byte) []byte {
116+
return append(crypto.Keccak256(b.Header().ParentHash[:]), suffix...)
117+
}
118+
119+
func (bh *stubBlockHooks) EncodeRLP(b *Block, w io.Writer) error {
120+
if _, err := w.Write(fakeBlockRLP(b, bh.suffix)); err != nil {
121+
return err
122+
}
123+
return bh.errEncode
124+
}
125+
126+
func (bh *stubBlockHooks) DecodeRLP(b *Block, s *rlp.Stream) error {
127+
r, err := s.Raw()
128+
if err != nil {
129+
return err
130+
}
131+
bh.gotRawRLPToDecode = r
132+
*b = bh.setBlockToOnUnmarshalOrDecode
133+
return bh.errDecode
134+
}
135+
107136
func TestHeaderHooks(t *testing.T) {
108137
TestOnlyClearRegisteredExtras()
109138
defer TestOnlyClearRegisteredExtras()
110139

111140
extras := RegisterExtras[
112141
stubHeaderHooks, *stubHeaderHooks,
113142
stubBodyHooks, *stubBodyHooks,
143+
stubBlockHooks, *stubBlockHooks,
114144
struct{}]()
115145
rng := ethtest.NewPseudoRand(13579)
116146

core/types/rlp_backwards_compat.libevm_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func TestHeaderRLPBackwardsCompatibility(t *testing.T) {
4343
RegisterExtras[
4444
NOOPHeaderHooks, *NOOPHeaderHooks,
4545
NOOPBodyHooks, *NOOPBodyHooks,
46+
NOOPBlockHooks, *NOOPBlockHooks,
4647
struct{}]()
4748
},
4849
},
@@ -88,7 +89,6 @@ func testHeaderRLPBackwardsCompatibility(t *testing.T) {
8889
ExcessBlobGas: rng.Uint64Ptr(),
8990
ParentBeaconRoot: rng.HashPtr(),
9091
}
91-
t.Logf("%T:\n%+v", hdr, hdr)
9292

9393
// WARNING: changing this hex might break backwards compatibility of RLP
9494
// encoding (i.e. block hashes might change)!

core/types/rlp_payload.libevm.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ import (
3939
// The payloads can be accessed via the [pseudo.Accessor] methods of the
4040
// [ExtraPayloads] returned by RegisterExtras. The default `SA` value accessed
4141
// in this manner will be a zero-value `SA` while the default value from a
42-
// [Header] is a non-nil `HPtr`. The latter guarantee ensures that hooks won't
43-
// be called on nil-pointer receivers.
42+
// [Header] is a non-nil `HPtr` and the default value from a [Block] is a non-nil
43+
// `BlockExtraPtr`. The latter guarantee ensures that hooks won't be called on nil-pointer receivers.
4444
func RegisterExtras[
4545
H any, HPtr interface {
4646
HeaderHooks
@@ -50,9 +50,13 @@ func RegisterExtras[
5050
BodyHooks
5151
*BodyExtra
5252
},
53+
BlockExtra any, BlockExtraPtr interface {
54+
BlockHooks
55+
*BlockExtra
56+
},
5357
SA any,
54-
]() ExtraPayloads[HPtr, BodyExtraPtr, SA] {
55-
extra := ExtraPayloads[HPtr, BodyExtraPtr, SA]{
58+
]() ExtraPayloads[HPtr, BodyExtraPtr, BlockExtraPtr, SA] {
59+
extra := ExtraPayloads[HPtr, BodyExtraPtr, BlockExtraPtr, SA]{
5660
Header: pseudo.NewAccessor[*Header, HPtr](
5761
(*Header).extraPayload,
5862
func(h *Header, t *pseudo.Type) { h.extra = t },
@@ -61,6 +65,10 @@ func RegisterExtras[
6165
(*Body).extraPayload,
6266
func(b *Body, t *pseudo.Type) { b.extra = t },
6367
),
68+
Block: pseudo.NewAccessor[*Block, BlockExtraPtr](
69+
(*Block).extraPayload,
70+
func(b *Block, t *pseudo.Type) { b.extra = t },
71+
),
6472
StateAccount: pseudo.NewAccessor[StateOrSlimAccount, SA](
6573
func(a StateOrSlimAccount) *pseudo.Type { return a.extra().payload() },
6674
func(a StateOrSlimAccount, t *pseudo.Type) { a.extra().t = t },
@@ -71,11 +79,12 @@ func RegisterExtras[
7179
var x SA
7280
return fmt.Sprintf("%T", x)
7381
}(),
74-
// The [ExtraPayloads] that we returns is based on [HPtr,BodyExtraPtr,SA], not
75-
// [H,BodyExtra,SA] so our constructors MUST match that. This guarantees that calls to
76-
// the [HeaderHooks] and [BodyHooks] methods will never be performed on a nil pointer.
77-
newHeader: pseudo.NewConstructor[H]().NewPointer, // i.e. non-nil HPtr
78-
newBody: pseudo.NewConstructor[BodyExtra]().NewPointer, // i.e. non-nil BodyExtraPtr
82+
// The [ExtraPayloads] that we returns is based on [HPtr,BodyExtraPtr,BlockExtraPtr,SA], not
83+
// [H,BodyExtra,BlockExtra,SA] so our constructors MUST match that. This guarantees that calls to
84+
// the [HeaderHooks], [BodyHooks] and [BlockHooks] methods will never be performed on a nil pointer.
85+
newHeader: pseudo.NewConstructor[H]().NewPointer, // i.e. non-nil HPtr
86+
newBody: pseudo.NewConstructor[BodyExtra]().NewPointer, // i.e. non-nil BodyExtraPtr
87+
newBlock: pseudo.NewConstructor[BlockExtra]().NewPointer, // i.e. non-nil BlockExtraPtr
7988
newStateAccount: pseudo.NewConstructor[SA]().Zero,
8089
cloneStateAccount: extra.cloneStateAccount,
8190
hooks: extra,
@@ -99,11 +108,13 @@ type extraConstructors struct {
99108
stateAccountType string
100109
newHeader func() *pseudo.Type
101110
newBody func() *pseudo.Type
111+
newBlock func() *pseudo.Type
102112
newStateAccount func() *pseudo.Type
103113
cloneStateAccount func(*StateAccountExtra) *StateAccountExtra
104114
hooks interface {
105115
hooksFromHeader(*Header) HeaderHooks
106116
hooksFromBody(*Body) BodyHooks
117+
hooksFromBlock(*Block) BlockHooks
107118
}
108119
}
109120

@@ -117,15 +128,16 @@ func (e *StateAccountExtra) clone() *StateAccountExtra {
117128
}
118129

119130
// ExtraPayloads provides strongly typed access to the extra payload carried by
120-
// [Header], [Body], [StateAccount], and [SlimAccount] structs. The only valid way to
131+
// [Header], [Body], [Block], [StateAccount], and [SlimAccount] structs. The only valid way to
121132
// construct an instance is by a call to [RegisterExtras].
122-
type ExtraPayloads[HPtr HeaderHooks, BodyExtraPtr BodyHooks, SA any] struct {
133+
type ExtraPayloads[HPtr HeaderHooks, BodyExtraPtr BodyHooks, BlockExtraPtr BlockHooks, SA any] struct {
123134
Header pseudo.Accessor[*Header, HPtr]
124135
Body pseudo.Accessor[*Body, BodyExtraPtr]
136+
Block pseudo.Accessor[*Block, BlockExtraPtr]
125137
StateAccount pseudo.Accessor[StateOrSlimAccount, SA] // Also provides [SlimAccount] access.
126138
}
127139

128-
func (ExtraPayloads[HPtr, BodyExtraPtr, SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
140+
func (ExtraPayloads[HPtr, BodyExtraPtr, BlockExtraPtr, SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
129141
v := pseudo.MustNewValue[SA](s.t)
130142
return &StateAccountExtra{
131143
t: pseudo.From(v.Get()).Type,

core/types/state_account.libevm_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestStateAccountRLP(t *testing.T) {
4949
RegisterExtras[
5050
NOOPHeaderHooks, *NOOPHeaderHooks,
5151
NOOPBodyHooks, *NOOPBodyHooks,
52+
NOOPBlockHooks, *NOOPBlockHooks,
5253
bool]()
5354
},
5455
acc: &StateAccount{
@@ -82,6 +83,7 @@ func TestStateAccountRLP(t *testing.T) {
8283
RegisterExtras[
8384
NOOPHeaderHooks, *NOOPHeaderHooks,
8485
NOOPBodyHooks, *NOOPBodyHooks,
86+
NOOPBlockHooks, *NOOPBlockHooks,
8587
bool]()
8688
},
8789
acc: &StateAccount{

core/types/state_account_storage.libevm_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
7676
e := types.RegisterExtras[
7777
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
7878
types.NOOPBodyHooks, *types.NOOPBodyHooks,
79+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
7980
bool]()
8081
e.StateAccount.Set(a, true)
8182
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
@@ -90,6 +91,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
9091
e := types.RegisterExtras[
9192
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
9293
types.NOOPBodyHooks, *types.NOOPBodyHooks,
94+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
9395
bool]()
9496
e.StateAccount.Set(a, false) // the explicit part
9597

@@ -105,6 +107,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
105107
e := types.RegisterExtras[
106108
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
107109
types.NOOPBodyHooks, *types.NOOPBodyHooks,
110+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
108111
bool]()
109112
// Note that `a` is reflected, unchanged (the implicit part).
110113
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
@@ -119,6 +122,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
119122
e := types.RegisterExtras[
120123
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
121124
types.NOOPBodyHooks, *types.NOOPBodyHooks,
125+
types.NOOPBlockHooks, *types.NOOPBlockHooks,
122126
arbitraryPayload]()
123127
p := arbitraryPayload{arbitraryData}
124128
e.StateAccount.Set(a, p)

0 commit comments

Comments
 (0)