Skip to content

Commit

Permalink
feat(core/types): BodyHooks for RLP overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
qdm12 committed Jan 20, 2025
1 parent 4f41ef3 commit 7ec5d04
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 12 deletions.
1 change: 1 addition & 0 deletions core/state/state.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func TestGetSetExtra(t *testing.T) {
payloads := types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
*accountExtra]().StateAccount

rng := ethtest.NewPseudoRand(42)
Expand Down
3 changes: 3 additions & 0 deletions core/state/state_object.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func TestStateObjectEmpty(t *testing.T) {
types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
bool]().StateAccount.Set(acc, false)
},
wantEmpty: true,
Expand All @@ -59,6 +60,7 @@ func TestStateObjectEmpty(t *testing.T) {
types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
bool]()
},
wantEmpty: true,
Expand All @@ -69,6 +71,7 @@ func TestStateObjectEmpty(t *testing.T) {
types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
bool]().StateAccount.Set(acc, true)
},
wantEmpty: false,
Expand Down
8 changes: 7 additions & 1 deletion core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ type Body struct {
Transactions []*Transaction
Uncles []*Header
Withdrawals []*Withdrawal `rlp:"optional"`

extra *pseudo.Type // See [RegisterExtras]
}

// Block represents an Ethereum block.
Expand Down Expand Up @@ -342,7 +344,11 @@ func (b *Block) encodeRLP(w io.Writer) error {
// Body returns the non-header content of the block.
// Note the returned data is not an independent copy.
func (b *Block) Body() *Body {
return &Body{b.transactions, b.uncles, b.withdrawals}
return &Body{
Transactions: b.transactions,
Uncles: b.uncles,
Withdrawals: b.withdrawals,
}
}

// Accessors for body data. These do not return a copy because the content
Expand Down
67 changes: 65 additions & 2 deletions core/types/block.libevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (h *Header) hooks() HeaderHooks {
return new(NOOPHeaderHooks)
}

func (e ExtraPayloads[HPtr, BlockExtraPtr, SA]) hooksFromHeader(h *Header) HeaderHooks {
func (e ExtraPayloads[HPtr, BlockExtraPtr, BodyExtraPtr, SA]) hooksFromHeader(h *Header) HeaderHooks {
return e.Header.Get(h)
}

Expand Down Expand Up @@ -125,7 +125,7 @@ func (b *Block) hooks() BlockHooks {
return new(NOOPBlockHooks)
}

func (e ExtraPayloads[HPtr, BlockExtraPtr, SA]) hooksFromBlock(b *Block) BlockHooks {
func (e ExtraPayloads[HPtr, BlockExtraPtr, BodyExtraPtr, SA]) hooksFromBlock(b *Block) BlockHooks {
return e.Block.Get(b)
}

Expand Down Expand Up @@ -181,3 +181,66 @@ func (b *Block) SetUncles(uncles []*Header) {
func (b *Block) SetTransactions(transactions Transactions) {
b.transactions = transactions
}

// BodyHooks are required for all types registered with [RegisterExtras] for
// [Body] payloads.
type BodyHooks interface {
EncodeRLP(*Body, io.Writer) error
DecodeRLP(*Body, *rlp.Stream) error
}

// hooks returns the Body's registered BodyHooks, if any, otherwise a
// [*NOOPBodyHooks] suitable for running the default behaviour.
func (b *Body) hooks() BodyHooks {
if r := registeredExtras; r.Registered() {
return r.Get().hooks.hooksFromBody(b)
}
return new(NOOPBodyHooks)
}

func (e ExtraPayloads[HPtr, BlockExtraPtr, BodyExtraPtr, SA]) hooksFromBody(b *Body) BodyHooks {
return e.Body.Get(b)
}

var _ interface {
rlp.Encoder
rlp.Decoder
} = (*Body)(nil)

// EncodeRLP implements the [rlp.Encoder] interface.
func (b *Body) EncodeRLP(w io.Writer) error {
return b.hooks().EncodeRLP(b, w)
}

// DecodeRLP implements the [rlp.Decoder] interface.
func (b *Body) DecodeRLP(s *rlp.Stream) error {
return b.hooks().DecodeRLP(b, s)
}

func (b *Body) extraPayload() *pseudo.Type {
r := registeredExtras
if !r.Registered() {
// See params.ChainConfig.extraPayload() for panic rationale.
panic(fmt.Sprintf("%T.extraPayload() called before RegisterExtras()", r))
}
if b.extra == nil {
b.extra = r.Get().newBody()
}
return b.extra
}

// NOOPBodyHooks implements [BodyHooks] such that they are equivalent to
// no type having been registered.
type NOOPBodyHooks struct{}

var _ BodyHooks = (*NOOPBodyHooks)(nil)

func (*NOOPBodyHooks) EncodeRLP(b *Body, w io.Writer) error {
type withoutMethods Body
return rlp.Encode(w, (*withoutMethods)(b))
}

func (*NOOPBodyHooks) DecodeRLP(b *Body, s *rlp.Stream) error {
type withoutMethods Body
return s.Decode((*withoutMethods)(b))
}
31 changes: 30 additions & 1 deletion core/types/block.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,40 @@ func (bh *stubBlockHooks) DecodeRLP(b *Block, s *rlp.Stream) error {
return bh.errDecode
}

type stubBodyHooks struct {
encoding []byte
gotRawRLPToDecode []byte
setBodyToOnUnmarshalOrDecode Body

errEncode, errDecode error
}

func (bh *stubBodyHooks) EncodeRLP(b *Body, w io.Writer) error {
if _, err := w.Write(bh.encoding); err != nil {
return err
}
return bh.errEncode
}

func (bh *stubBodyHooks) DecodeRLP(b *Body, s *rlp.Stream) error {
r, err := s.Raw()
if err != nil {
return err
}
bh.gotRawRLPToDecode = r
*b = bh.setBodyToOnUnmarshalOrDecode
return bh.errDecode
}

func TestHeaderHooks(t *testing.T) {
TestOnlyClearRegisteredExtras()
defer TestOnlyClearRegisteredExtras()

extras := RegisterExtras[stubHeaderHooks, *stubHeaderHooks, stubBlockHooks, *stubBlockHooks, struct{}]()
extras := RegisterExtras[
stubHeaderHooks, *stubHeaderHooks,
stubBlockHooks, *stubBlockHooks,
stubBodyHooks, *stubBodyHooks,
struct{}]()
rng := ethtest.NewPseudoRand(13579)

suffix := rng.Bytes(8)
Expand Down
1 change: 1 addition & 0 deletions core/types/rlp_backwards_compat.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func TestHeaderRLPBackwardsCompatibility(t *testing.T) {
RegisterExtras[
NOOPHeaderHooks, *NOOPHeaderHooks,
NOOPBlockHooks, *NOOPBlockHooks,
NOOPBodyHooks, *NOOPBodyHooks,
struct{}]()
},
},
Expand Down
28 changes: 20 additions & 8 deletions core/types/rlp_payload.libevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,13 @@ func RegisterExtras[
BlockHooks
*BlockExtra
},
BodyExtra any, BodyExtraPtr interface {
BodyHooks
*BodyExtra
},
SA any,
]() ExtraPayloads[HPtr, BlockExtraPtr, SA] {
extra := ExtraPayloads[HPtr, BlockExtraPtr, SA]{
]() ExtraPayloads[HPtr, BlockExtraPtr, BodyExtraPtr, SA] {
extra := ExtraPayloads[HPtr, BlockExtraPtr, BodyExtraPtr, SA]{
Header: pseudo.NewAccessor[*Header, HPtr](
(*Header).extraPayload,
func(h *Header, t *pseudo.Type) { h.extra = t },
Expand All @@ -61,6 +65,10 @@ func RegisterExtras[
(*Block).extraPayload,
func(b *Block, t *pseudo.Type) { b.extra = t },
),
Body: pseudo.NewAccessor[*Body, BodyExtraPtr](
(*Body).extraPayload,
func(b *Body, t *pseudo.Type) { b.extra = t },
),
StateAccount: pseudo.NewAccessor[StateOrSlimAccount, SA](
func(a StateOrSlimAccount) *pseudo.Type { return a.extra().payload() },
func(a StateOrSlimAccount, t *pseudo.Type) { a.extra().t = t },
Expand All @@ -71,11 +79,12 @@ func RegisterExtras[
var x SA
return fmt.Sprintf("%T", x)
}(),
// The [ExtraPayloads] that we returns is based on [HPtr,BlockExtraPtr,SA], not
// [H,BlockExtra,SA] so our constructors MUST match that. This guarantees that calls to
// the [HeaderHooks] and [BlockHooks] methods will never be performed on a nil pointer.
// The [ExtraPayloads] that we returns is based on [HPtr,BlockExtraPtr,BodyExtraPtr,SA], not
// [H,BlockExtra,BodyExtra,SA] so our constructors MUST match that. This guarantees that calls to
// the [HeaderHooks], [BlockHooks] and [BodyHooks] methods will never be performed on a nil pointer.
newHeader: pseudo.NewConstructor[H]().NewPointer, // i.e. non-nil HPtr
newBlock: pseudo.NewConstructor[BlockExtra]().NewPointer, // i.e. non-nil BlockExtraPtr
newBody: pseudo.NewConstructor[BodyExtra]().NewPointer, // i.e. non-nil BodyExtraPtr
newStateAccount: pseudo.NewConstructor[SA]().Zero,
cloneStateAccount: extra.cloneStateAccount,
hooks: extra,
Expand All @@ -99,11 +108,13 @@ type extraConstructors struct {
stateAccountType string
newHeader func() *pseudo.Type
newBlock func() *pseudo.Type
newBody func() *pseudo.Type
newStateAccount func() *pseudo.Type
cloneStateAccount func(*StateAccountExtra) *StateAccountExtra
hooks interface {
hooksFromHeader(*Header) HeaderHooks
hooksFromBlock(*Block) BlockHooks
hooksFromBody(*Body) BodyHooks
}
}

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

// ExtraPayloads provides strongly typed access to the extra payload carried by
// [Header], [Block], [StateAccount], and [SlimAccount] structs. The only valid way to
// [Header], [Block], [Body], [StateAccount], and [SlimAccount] structs. The only valid way to
// construct an instance is by a call to [RegisterExtras].
type ExtraPayloads[HPtr HeaderHooks, BlockExtraPtr BlockHooks, SA any] struct {
type ExtraPayloads[HPtr HeaderHooks, BlockExtraPtr BlockHooks, BodyExtraPtr BodyHooks, SA any] struct {
Header pseudo.Accessor[*Header, HPtr]
Block pseudo.Accessor[*Block, BlockExtraPtr]
Body pseudo.Accessor[*Body, BodyExtraPtr]
StateAccount pseudo.Accessor[StateOrSlimAccount, SA] // Also provides [SlimAccount] access.
}

func (ExtraPayloads[HPtr, BlockExtraPtr, SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
func (ExtraPayloads[HPtr, BlockExtraPtr, BodyExtraPtr, SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
v := pseudo.MustNewValue[SA](s.t)
return &StateAccountExtra{
t: pseudo.From(v.Get()).Type,
Expand Down
2 changes: 2 additions & 0 deletions core/types/state_account.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func TestStateAccountRLP(t *testing.T) {
RegisterExtras[
NOOPHeaderHooks, *NOOPHeaderHooks,
NOOPBlockHooks, *NOOPBlockHooks,
NOOPBodyHooks, *NOOPBodyHooks,
bool]()
},
acc: &StateAccount{
Expand Down Expand Up @@ -82,6 +83,7 @@ func TestStateAccountRLP(t *testing.T) {
RegisterExtras[
NOOPHeaderHooks, *NOOPHeaderHooks,
NOOPBlockHooks, *NOOPBlockHooks,
NOOPBodyHooks, *NOOPBodyHooks,
bool]()
},
acc: &StateAccount{
Expand Down
4 changes: 4 additions & 0 deletions core/types/state_account_storage.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
e := types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
bool]()
e.StateAccount.Set(a, true)
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
Expand All @@ -90,6 +91,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
e := types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
bool]()
e.StateAccount.Set(a, false) // the explicit part

Expand All @@ -105,6 +107,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
e := types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
bool]()
// Note that `a` is reflected, unchanged (the implicit part).
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
Expand All @@ -119,6 +122,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
e := types.RegisterExtras[
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
types.NOOPBlockHooks, *types.NOOPBlockHooks,
types.NOOPBodyHooks, *types.NOOPBodyHooks,
arbitraryPayload]()
p := arbitraryPayload{arbitraryData}
e.StateAccount.Set(a, p)
Expand Down

0 comments on commit 7ec5d04

Please sign in to comment.