diff --git a/core/state/state.libevm.go b/core/state/state.libevm.go index 9cdcf047eabd..369fea975b9c 100644 --- a/core/state/state.libevm.go +++ b/core/state/state.libevm.go @@ -19,40 +19,41 @@ package state import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/libevm/pseudo" ) // GetExtra returns the extra payload from the [types.StateAccount] associated -// with the address, or a zero-value `SA` if not found. The -// [types.ExtraPayloads] MUST be sourced from [types.RegisterExtras]. -func GetExtra[SA any](s *StateDB, p types.ExtraPayloads[SA], addr common.Address) SA { +// with the address, or a zero-value `SA` if not found. The [pseudo.Accessor] +// MUST be sourced from [types.RegisterExtras]. +func GetExtra[SA any](s *StateDB, a pseudo.Accessor[types.StateOrSlimAccount, SA], addr common.Address) SA { stateObject := s.getStateObject(addr) if stateObject != nil { - return p.StateAccount.Get(&stateObject.data) + return a.Get(&stateObject.data) } var zero SA return zero } // SetExtra sets the extra payload for the address. See [GetExtra] for details. -func SetExtra[SA any](s *StateDB, p types.ExtraPayloads[SA], addr common.Address, extra SA) { +func SetExtra[SA any](s *StateDB, a pseudo.Accessor[types.StateOrSlimAccount, SA], addr common.Address, extra SA) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { - setExtraOnObject(stateObject, p, addr, extra) + setExtraOnObject(stateObject, a, addr, extra) } } -func setExtraOnObject[SA any](s *stateObject, p types.ExtraPayloads[SA], addr common.Address, extra SA) { +func setExtraOnObject[SA any](s *stateObject, a pseudo.Accessor[types.StateOrSlimAccount, SA], addr common.Address, extra SA) { s.db.journal.append(extraChange[SA]{ - payloads: p, + accessor: a, account: &addr, - prev: p.StateAccount.Get(&s.data), + prev: a.Get(&s.data), }) - p.StateAccount.Set(&s.data, extra) + a.Set(&s.data, extra) } // extraChange is a [journalEntry] for [SetExtra] / [setExtraOnObject]. type extraChange[SA any] struct { - payloads types.ExtraPayloads[SA] + accessor pseudo.Accessor[types.StateOrSlimAccount, SA] account *common.Address prev SA } @@ -60,5 +61,5 @@ type extraChange[SA any] struct { func (e extraChange[SA]) dirtied() *common.Address { return e.account } func (e extraChange[SA]) revert(s *StateDB) { - e.payloads.StateAccount.Set(&s.getStateObject(*e.account).data, e.prev) + e.accessor.Set(&s.getStateObject(*e.account).data, e.prev) } diff --git a/core/state/state.libevm_test.go b/core/state/state.libevm_test.go index 7d7ab1908089..8b682cb49b7a 100644 --- a/core/state/state.libevm_test.go +++ b/core/state/state.libevm_test.go @@ -45,7 +45,7 @@ func TestGetSetExtra(t *testing.T) { t.Cleanup(types.TestOnlyClearRegisteredExtras) // Just as its Data field is a pointer, the registered type is a pointer to // test deep copying. - payloads := types.RegisterExtras[*accountExtra]() + payloads := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, *accountExtra]().StateAccount rng := ethtest.NewPseudoRand(42) addr := rng.Address() @@ -87,7 +87,7 @@ func TestGetSetExtra(t *testing.T) { Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash[:], } - payloads.StateAccount.Set(want, extra) + payloads.Set(want, extra) if diff := cmp.Diff(want, got); diff != "" { t.Errorf("types.FullAccount(%T.Account()) diff (-want +got):\n%s", iter, diff) diff --git a/core/state/state_object.libevm_test.go b/core/state/state_object.libevm_test.go index cba90bf80c44..d04de5a1aaa3 100644 --- a/core/state/state_object.libevm_test.go +++ b/core/state/state_object.libevm_test.go @@ -46,21 +46,21 @@ func TestStateObjectEmpty(t *testing.T) { { name: "explicit false bool", registerAndSet: func(acc *types.StateAccount) { - types.RegisterExtras[bool]().StateAccount.Set(acc, false) + types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]().StateAccount.Set(acc, false) }, wantEmpty: true, }, { name: "implicit false bool", registerAndSet: func(*types.StateAccount) { - types.RegisterExtras[bool]() + types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]() }, wantEmpty: true, }, { name: "true bool", registerAndSet: func(acc *types.StateAccount) { - types.RegisterExtras[bool]().StateAccount.Set(acc, true) + types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]().StateAccount.Set(acc, true) }, wantEmpty: false, }, diff --git a/core/types/block.go b/core/types/block.go index 6be163bd60c0..5272d4c2297d 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -28,6 +28,7 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" + "github.com/ava-labs/libevm/libevm/pseudo" "github.com/ava-labs/libevm/rlp" ) @@ -93,6 +94,8 @@ type Header struct { // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + + extra *pseudo.Type // See RegisterExtras() } // field type overrides for gencodec diff --git a/core/types/block.libevm.go b/core/types/block.libevm.go index adc5107a1369..1a4dca038186 100644 --- a/core/types/block.libevm.go +++ b/core/types/block.libevm.go @@ -17,13 +17,74 @@ package types import ( + "fmt" "io" + "github.com/ava-labs/libevm/libevm/pseudo" "github.com/ava-labs/libevm/rlp" ) +// HeaderHooks are required for all types registered with [RegisterExtras] for +// [Header] payloads. +type HeaderHooks interface { + EncodeRLP(*Header, io.Writer) error + DecodeRLP(*Header, *rlp.Stream) error +} + +var _ interface { + rlp.Encoder + rlp.Decoder +} = (*Header)(nil) + +// EncodeRLP implements the [rlp.Encoder] interface. func (h *Header) EncodeRLP(w io.Writer) error { + if r := registeredExtras; r.Registered() { + return r.Get().hooks.hooksFromHeader(h).EncodeRLP(h, w) + } return h.encodeRLP(w) } -var _ rlp.Encoder = (*Header)(nil) +// decodeHeaderRLPDirectly bypasses the [Header.DecodeRLP] method to avoid +// infinite recursion. +func decodeHeaderRLPDirectly(h *Header, s *rlp.Stream) error { + type withoutMethods Header + return s.Decode((*withoutMethods)(h)) +} + +// DecodeRLP implements the [rlp.Decoder] interface. +func (h *Header) DecodeRLP(s *rlp.Stream) error { + if r := registeredExtras; r.Registered() { + return r.Get().hooks.hooksFromHeader(h).DecodeRLP(h, s) + } + return decodeHeaderRLPDirectly(h, s) +} + +func (e ExtraPayloads[HPtr, SA]) hooksFromHeader(h *Header) HeaderHooks { + return e.Header.Get(h) +} + +func (h *Header) 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 h.extra == nil { + h.extra = r.Get().newHeader() + } + return h.extra +} + +// NOOPHeaderHooks implements [HeaderHooks] such that they are equivalent to +// no type having been registered. +type NOOPHeaderHooks struct{} + +var _ HeaderHooks = (*NOOPHeaderHooks)(nil) + +func (*NOOPHeaderHooks) EncodeRLP(h *Header, w io.Writer) error { + return h.encodeRLP(w) +} + +func (*NOOPHeaderHooks) DecodeRLP(h *Header, s *rlp.Stream) error { + return decodeHeaderRLPDirectly(h, s) +} diff --git a/core/types/block.libevm_test.go b/core/types/block.libevm_test.go new file mode 100644 index 000000000000..547401362396 --- /dev/null +++ b/core/types/block.libevm_test.go @@ -0,0 +1,113 @@ +// Copyright 2024 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The libevm additions are distributed in the hope that they will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see +// . + +package types_test + +import ( + "errors" + "io" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + . "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" + "github.com/ava-labs/libevm/libevm/ethtest" + "github.com/ava-labs/libevm/rlp" +) + +type stubHeaderHooks struct { + rlpSuffix []byte + gotRawRLPToDecode []byte + setHeaderToOnDecode Header + + errEncode, errDecode error +} + +func fakeHeaderRLP(h *Header, suffix []byte) []byte { + return append(crypto.Keccak256(h.ParentHash[:]), suffix...) +} + +func (hh *stubHeaderHooks) EncodeRLP(h *Header, w io.Writer) error { + if _, err := w.Write(fakeHeaderRLP(h, hh.rlpSuffix)); err != nil { + return err + } + return hh.errEncode +} + +func (hh *stubHeaderHooks) DecodeRLP(h *Header, s *rlp.Stream) error { + r, err := s.Raw() + if err != nil { + return err + } + hh.gotRawRLPToDecode = r + *h = hh.setHeaderToOnDecode + return hh.errDecode +} + +func TestHeaderHooks(t *testing.T) { + TestOnlyClearRegisteredExtras() + defer TestOnlyClearRegisteredExtras() + + extras := RegisterExtras[stubHeaderHooks, *stubHeaderHooks, struct{}]() + rng := ethtest.NewPseudoRand(13579) + + t.Run("EncodeRLP", func(t *testing.T) { + suffix := rng.Bytes(8) + + hdr := &Header{ + ParentHash: rng.Hash(), + } + extras.Header.Get(hdr).rlpSuffix = append([]byte{}, suffix...) + + got, err := rlp.EncodeToBytes(hdr) + require.NoError(t, err, "rlp.EncodeToBytes(%T)", hdr) + assert.Equal(t, fakeHeaderRLP(hdr, suffix), got) + }) + + t.Run("DecodeRLP", func(t *testing.T) { + input, err := rlp.EncodeToBytes(rng.Bytes(8)) + require.NoError(t, err) + + hdr := new(Header) + stub := &stubHeaderHooks{ + setHeaderToOnDecode: Header{ + Extra: []byte("arr4n was here"), + }, + } + extras.Header.Set(hdr, stub) + err = rlp.DecodeBytes(input, hdr) + require.NoErrorf(t, err, "rlp.DecodeBytes(%#x)", input) + + assert.Equal(t, input, stub.gotRawRLPToDecode, "raw RLP received by hooks") + assert.Equalf(t, &stub.setHeaderToOnDecode, hdr, "%T after RLP decoding with hook", hdr) + }) + + t.Run("error_propagation", func(t *testing.T) { + errEncode := errors.New("uh oh") + errDecode := errors.New("something bad happened") + + hdr := new(Header) + extras.Header.Set(hdr, &stubHeaderHooks{ + errEncode: errEncode, + errDecode: errDecode, + }) + + assert.Equal(t, errEncode, rlp.Encode(io.Discard, hdr), "via rlp.Encode()") + assert.Equal(t, errDecode, rlp.DecodeBytes([]byte{0}, hdr), "via rlp.DecodeBytes()") + }) +} diff --git a/core/types/rlp_backwards_compat.libevm_test.go b/core/types/rlp_backwards_compat.libevm_test.go index a1301281556e..8b22eac29752 100644 --- a/core/types/rlp_backwards_compat.libevm_test.go +++ b/core/types/rlp_backwards_compat.libevm_test.go @@ -29,6 +29,34 @@ import ( ) func TestHeaderRLPBackwardsCompatibility(t *testing.T) { + tests := []struct { + name string + register func() + }{ + { + name: "no registered extras", + register: func() {}, + }, + { + name: "no-op header hooks", + register: func() { + RegisterExtras[NOOPHeaderHooks, *NOOPHeaderHooks, struct{}]() + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + TestOnlyClearRegisteredExtras() + defer TestOnlyClearRegisteredExtras() + tt.register() + testHeaderRLPBackwardsCompatibility(t) + }) + } +} + +//nolint:thelper +func testHeaderRLPBackwardsCompatibility(t *testing.T) { // This is a deliberate change-detector test that locks in backwards // compatibility of RLP encoding. rng := ethtest.NewPseudoRand(42) @@ -62,10 +90,19 @@ func TestHeaderRLPBackwardsCompatibility(t *testing.T) { // WARNING: changing this hex might break backwards compatibility of RLP // encoding (i.e. block hashes might change)! const wantHex = `f9029aa01a571e7e4d774caf46053201cfe0001b3c355ffcc93f510e671e8809741f0eeda0756095410506ec72a2c287fe83ebf68efb0be177e61acec1c985277e90e52087941bfc3bc193012ba58912c01fb35a3454831a8971a00bc9f064144eb5965c5e5d1020f9f90392e7e06ded9225966abc7c754b410e61a0d942eab201424f4320ec1e1ffa9390baf941629b9349977b5d48e0502dbb9386a035d9d550a9c113f78689b4c161c4605609bb57b83061914c42ad244daa7fc38eb901004b31d39ae246d689f23176d679a62ff328f530407cbafd0146f45b2ed635282e2812f2705bfffe52576a6fb31df817f29efac71fa56b8e133334079f8e2a8fd2055451571021506f27190adb52a1313f6d28c77d66ae1aa3d3d6757a762476f4c8a2b7b2a37079a4b6a15d1bc44161190c82d5e1c8b55e05c7354f1e5f6512924c941fb3d93667dc3a8c304a3c164e6525dfc99b5f474110c5059485732153e20300c3482832d07b65f97958360da414cb438ce252aec6c2718d155798390a6c6782181d1bac1dd64cd956332b008412ddc735f2994e297c8a088c6bb4c637542295ba3cbc3cd399c8127076f4d834d74d5b11a36b6d02e2fe3a583216aa4ccea0f052df9a96e7a454256bebabdfc38c429079f25913e0f1d7416b2f056c4a115f88b85f0e9fd6d25717881f03d9985060087c88a2c54269dfd07ca388eb8f974b42a412da90c757012bf5479896165caf573cf82fb3a0aa10f6ebf6b62bef8ed36b8ea3d4b1ddb80c99afafa37cb8f3393eb6d802f5bc886c8cd6bcd168a7e0886d5b1345d948b818a0061a7182ff228a4e66bade4717e6f4d318ac98fca12a053af6f98805a764fb5d8890ed9cab2c5229908891c7e2f71857c77ca0523cb6f654ef3fc7294c7768cddd9ccf4bcda3066d382675f37dd1a18507b5fb` - want, err := hex.DecodeString(wantHex) + wantRLP, err := hex.DecodeString(wantHex) require.NoError(t, err, "hex.DecodeString()") - got, err := rlp.EncodeToBytes(hdr) - require.NoErrorf(t, err, "rlp.EncodeToBytes(%T)", hdr) - assert.Equalf(t, want, got, "rlp.EncodeToBytes(%T)", hdr) + t.Run("Encode", func(t *testing.T) { + got, err := rlp.EncodeToBytes(hdr) + require.NoErrorf(t, err, "rlp.EncodeToBytes(%T)", hdr) + assert.Equalf(t, wantRLP, got, "rlp.EncodeToBytes(%T)", hdr) + }) + + t.Run("Decode", func(t *testing.T) { + got := new(Header) + err := rlp.DecodeBytes(wantRLP, got) + require.NoErrorf(t, err, "rlp.DecodeBytes(..., %T)", hdr) + assert.Equal(t, hdr, got) + }) } diff --git a/core/types/rlp_payload.libevm.go b/core/types/rlp_payload.libevm.go index 12dbab3e08fb..90d5b7e43e33 100644 --- a/core/types/rlp_payload.libevm.go +++ b/core/types/rlp_payload.libevm.go @@ -26,22 +26,36 @@ import ( "github.com/ava-labs/libevm/rlp" ) -// RegisterExtras registers the type `SA` to be carried as an extra payload in -// [StateAccount] and [SlimAccount] structs. It is expected to be called in an -// `init()` function and MUST NOT be called more than once. +// RegisterExtras registers the type `HPtr` to be carried as an extra payload in +// [Header] structs and the type `SA` in [StateAccount] and [SlimAccount] +// structs. It is expected to be called in an `init()` function and MUST NOT be +// called more than once. // -// The payload will be treated as an extra struct field for the purposes of RLP -// encoding and decoding. RLP handling is plumbed through to the `SA` via the -// [StateAccountExtra] that holds it such that it acts as if there were a field -// of type `SA` in all StateAccount and SlimAccount structs. +// The `SA` payload will be treated as an extra struct field for the purposes of +// RLP encoding and decoding. RLP handling is plumbed through to the `SA` via +// the [StateAccountExtra] that holds it such that it acts as if there were a +// field of type `SA` in all StateAccount and SlimAccount structs. // -// The payload can be accessed via the [ExtraPayloads.FromPayloadCarrier] method -// of the accessor returned by RegisterExtras. -func RegisterExtras[SA any]() ExtraPayloads[SA] { - extra := ExtraPayloads[SA]{ - StateAccount: pseudo.NewAccessor[ExtraPayloadCarrier, SA]( - func(a ExtraPayloadCarrier) *pseudo.Type { return a.extra().payload() }, - func(a ExtraPayloadCarrier, t *pseudo.Type) { a.extra().t = t }, +// The payloads can be accessed via the [pseudo.Accessor] methods of the +// [ExtraPayloads] returned by RegisterExtras. The default `SA` value accessed +// in this manner will be a zero-value `SA` while the default value from a +// [Header] is a non-nil `HPtr`. The latter guarantee ensures that hooks won't +// be called on nil-pointer receivers. +func RegisterExtras[ + H any, HPtr interface { + HeaderHooks + *H + }, + SA any, +]() ExtraPayloads[HPtr, SA] { + extra := ExtraPayloads[HPtr, SA]{ + Header: pseudo.NewAccessor[*Header, HPtr]( + (*Header).extraPayload, + func(h *Header, t *pseudo.Type) { h.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 }, ), } registeredExtras.MustRegister(&extraConstructors{ @@ -49,8 +63,13 @@ func RegisterExtras[SA any]() ExtraPayloads[SA] { var x SA return fmt.Sprintf("%T", x) }(), + // The [ExtraPayloads] that we returns is based on [HPtr,SA], not [H,SA] + // so our constructors MUST match that. This guarantees that calls to + // the [HeaderHooks] methods will never be performed on a nil pointer. + newHeader: pseudo.NewConstructor[H]().NewPointer, // i.e. non-nil HPtr newStateAccount: pseudo.NewConstructor[SA]().Zero, cloneStateAccount: extra.cloneStateAccount, + hooks: extra, }) return extra } @@ -68,9 +87,12 @@ func TestOnlyClearRegisteredExtras() { var registeredExtras register.AtMostOnce[*extraConstructors] type extraConstructors struct { - stateAccountType string - newStateAccount func() *pseudo.Type - cloneStateAccount func(*StateAccountExtra) *StateAccountExtra + stateAccountType string + newHeader, newStateAccount func() *pseudo.Type + cloneStateAccount func(*StateAccountExtra) *StateAccountExtra + hooks interface { + hooksFromHeader(*Header) HeaderHooks + } } func (e *StateAccountExtra) clone() *StateAccountExtra { @@ -83,32 +105,33 @@ func (e *StateAccountExtra) clone() *StateAccountExtra { } // ExtraPayloads provides strongly typed access to the extra payload carried by -// [StateAccount] and [SlimAccount] structs. The only valid way to construct an -// instance is by a call to [RegisterExtras]. -type ExtraPayloads[SA any] struct { - StateAccount pseudo.Accessor[ExtraPayloadCarrier, SA] // Also provides [SlimAccount] access. +// [Header], [StateAccount], and [SlimAccount] structs. The only valid way to +// construct an instance is by a call to [RegisterExtras]. +type ExtraPayloads[HPtr HeaderHooks, SA any] struct { + Header pseudo.Accessor[*Header, HPtr] + StateAccount pseudo.Accessor[StateOrSlimAccount, SA] // Also provides [SlimAccount] access. } -func (ExtraPayloads[SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra { +func (ExtraPayloads[HPtr, SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra { v := pseudo.MustNewValue[SA](s.t) return &StateAccountExtra{ t: pseudo.From(v.Get()).Type, } } -// ExtraPayloadCarrier is implemented by both [StateAccount] and [SlimAccount], +// StateOrSlimAccount is implemented by both [StateAccount] and [SlimAccount], // allowing for their [StateAccountExtra] payloads to be accessed in a type-safe // manner by [ExtraPayloads] instances. -type ExtraPayloadCarrier interface { +type StateOrSlimAccount interface { extra() *StateAccountExtra } -var _ = []ExtraPayloadCarrier{ +var _ = []StateOrSlimAccount{ (*StateAccount)(nil), (*SlimAccount)(nil), } -// A StateAccountExtra carries the extra payload, if any, registered with +// A StateAccountExtra carries the `SA` extra payload, if any, registered with // [RegisterExtras]. It SHOULD NOT be used directly; instead use the // [ExtraPayloads] accessor returned by RegisterExtras. type StateAccountExtra struct { diff --git a/core/types/state_account.libevm_test.go b/core/types/state_account.libevm_test.go index 89ae3d2e5703..6fd601ef9d7e 100644 --- a/core/types/state_account.libevm_test.go +++ b/core/types/state_account.libevm_test.go @@ -46,7 +46,7 @@ func TestStateAccountRLP(t *testing.T) { explicitFalseBoolean := test{ name: "explicit false-boolean extra", register: func() { - RegisterExtras[bool]() + RegisterExtras[NOOPHeaderHooks, *NOOPHeaderHooks, bool]() }, acc: &StateAccount{ Nonce: 0x444444, @@ -76,7 +76,7 @@ func TestStateAccountRLP(t *testing.T) { { name: "true-boolean extra", register: func() { - RegisterExtras[bool]() + RegisterExtras[NOOPHeaderHooks, *NOOPHeaderHooks, bool]() }, acc: &StateAccount{ Nonce: 0x444444, diff --git a/core/types/state_account_storage.libevm_test.go b/core/types/state_account_storage.libevm_test.go index 0c3568997456..9db9ee552d81 100644 --- a/core/types/state_account_storage.libevm_test.go +++ b/core/types/state_account_storage.libevm_test.go @@ -73,7 +73,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) { { name: "true-boolean payload", registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) { - e := types.RegisterExtras[bool]() + e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]() e.StateAccount.Set(a, true) return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper assert.Truef(t, e.StateAccount.Get(got), "") @@ -84,7 +84,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) { { name: "explicit false-boolean payload", registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) { - e := types.RegisterExtras[bool]() + e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]() e.StateAccount.Set(a, false) // the explicit part return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper @@ -96,7 +96,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) { { name: "implicit false-boolean payload", registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) { - e := types.RegisterExtras[bool]() + e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]() // Note that `a` is reflected, unchanged (the implicit part). return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper assert.Falsef(t, e.StateAccount.Get(got), "") @@ -107,7 +107,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) { { name: "arbitrary payload", registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) { - e := types.RegisterExtras[arbitraryPayload]() + e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, arbitraryPayload]() p := arbitraryPayload{arbitraryData} e.StateAccount.Set(a, p) return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper diff --git a/params/config.libevm.go b/params/config.libevm.go index e75956e0ea60..f8a153f3d988 100644 --- a/params/config.libevm.go +++ b/params/config.libevm.go @@ -186,7 +186,8 @@ func (c *ChainConfig) addRulesExtra(r *Rules, blockNum *big.Int, isMerge bool, t func (c *ChainConfig) extraPayload() *pseudo.Type { if !registeredExtras.Registered() { // This will only happen if someone constructs an [ExtraPayloads] - // directly, without a call to [RegisterExtras]. + // directly, without a call to [RegisterExtras]. It would also panic on + // the next call anyway so this is at least a useful message. // // See https://google.github.io/styleguide/go/best-practices#when-to-panic panic(fmt.Sprintf("%T.ExtraPayload() called before RegisterExtras()", c))