Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions core/state/state.libevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
func GetExtra[SA any](s *StateDB, p types.ExtraPayloads[SA], addr common.Address) SA {
stateObject := s.getStateObject(addr)
if stateObject != nil {
return p.FromPayloadCarrier(&stateObject.data)
return p.StateAccount.Get(&stateObject.data)
}
var zero SA
return zero
Expand All @@ -45,9 +45,9 @@ func setExtraOnObject[SA any](s *stateObject, p types.ExtraPayloads[SA], addr co
s.db.journal.append(extraChange[SA]{
payloads: p,
account: &addr,
prev: p.FromPayloadCarrier(&s.data),
prev: p.StateAccount.Get(&s.data),
})
p.SetOnPayloadCarrier(&s.data, extra)
p.StateAccount.Set(&s.data, extra)
}

// extraChange is a [journalEntry] for [SetExtra] / [setExtraOnObject].
Expand All @@ -60,5 +60,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.SetOnPayloadCarrier(&s.getStateObject(*e.account).data, e.prev)
e.payloads.StateAccount.Set(&s.getStateObject(*e.account).data, e.prev)
}
2 changes: 1 addition & 1 deletion core/state/state.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestGetSetExtra(t *testing.T) {
Root: types.EmptyRootHash,
CodeHash: types.EmptyCodeHash[:],
}
payloads.SetOnPayloadCarrier(want, extra)
payloads.StateAccount.Set(want, extra)

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("types.FullAccount(%T.Account()) diff (-want +got):\n%s", iter, diff)
Expand Down
4 changes: 2 additions & 2 deletions core/state/state_object.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestStateObjectEmpty(t *testing.T) {
{
name: "explicit false bool",
registerAndSet: func(acc *types.StateAccount) {
types.RegisterExtras[bool]().SetOnPayloadCarrier(acc, false)
types.RegisterExtras[bool]().StateAccount.Set(acc, false)
},
wantEmpty: true,
},
Expand All @@ -60,7 +60,7 @@ func TestStateObjectEmpty(t *testing.T) {
{
name: "true bool",
registerAndSet: func(acc *types.StateAccount) {
types.RegisterExtras[bool]().SetOnPayloadCarrier(acc, true)
types.RegisterExtras[bool]().StateAccount.Set(acc, true)
},
wantEmpty: false,
},
Expand Down
30 changes: 7 additions & 23 deletions core/types/rlp_payload.libevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ import (
// The payload can be accessed via the [ExtraPayloads.FromPayloadCarrier] method
// of the accessor returned by RegisterExtras.
func RegisterExtras[SA any]() ExtraPayloads[SA] {
var extra 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 },
),
}
registeredExtras.MustRegister(&extraConstructors{
stateAccountType: func() string {
var x SA
Expand Down Expand Up @@ -81,7 +86,7 @@ func (e *StateAccountExtra) clone() *StateAccountExtra {
// [StateAccount] and [SlimAccount] structs. The only valid way to construct an
// instance is by a call to [RegisterExtras].
type ExtraPayloads[SA any] struct {
_ struct{} // make godoc show unexported fields so nobody tries to make their own instance ;)
StateAccount pseudo.Accessor[ExtraPayloadCarrier, SA] // Also provides [SlimAccount] access.
}

func (ExtraPayloads[SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
Expand All @@ -103,27 +108,6 @@ var _ = []ExtraPayloadCarrier{
(*SlimAccount)(nil),
}

// FromPayloadCarrier returns the carriers's payload.
func (ExtraPayloads[SA]) FromPayloadCarrier(a ExtraPayloadCarrier) SA {
return pseudo.MustNewValue[SA](a.extra().payload()).Get()
}

// PointerFromPayloadCarrier returns a pointer to the carriers's extra payload.
// This is guaranteed to be non-nil.
//
// Note that copying a [StateAccount] or [SlimAccount] by dereferencing a
// pointer will result in a shallow copy and that the *SA returned here will
// therefore be shared by all copies. If this is not the desired behaviour, use
// [StateAccount.Copy] or [ExtraPayloads.SetOnPayloadCarrier].
func (ExtraPayloads[SA]) PointerFromPayloadCarrier(a ExtraPayloadCarrier) *SA {
return pseudo.MustPointerTo[SA](a.extra().payload()).Value.Get()
}

// SetOnPayloadCarrier sets the carriers's payload.
func (ExtraPayloads[SA]) SetOnPayloadCarrier(a ExtraPayloadCarrier, val SA) {
a.extra().t = pseudo.From(val).Type
}

// A StateAccountExtra carries the extra payload, if any, registered with
// [RegisterExtras]. It SHOULD NOT be used directly; instead use the
// [ExtraPayloads] accessor returned by RegisterExtras.
Expand Down
76 changes: 36 additions & 40 deletions core/types/state_account_storage.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,72 +51,68 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
arbitrary = common.HexToHash("0x94eecff1444ab69437636630918c15596e001b30b973f03e06006ae20aa6e307")
)

// An assertion is the actual test to be performed. It is returned upon
// registration, instead of being a standalone field in the test, because
// each one uses a different generic parameter.
type assertion func(*testing.T, *types.StateAccount)
tests := []struct {
name string
registerAndSetExtra func(*types.StateAccount) *types.StateAccount
assertExtra func(*testing.T, *types.StateAccount)
registerAndSetExtra func(*types.StateAccount) (*types.StateAccount, assertion)
wantTrieHash common.Hash
}{
{
name: "vanilla geth",
registerAndSetExtra: func(a *types.StateAccount) *types.StateAccount {
return a
},
assertExtra: func(t *testing.T, a *types.StateAccount) {
t.Helper()
assert.Truef(t, a.Extra.Equal(nil), "%T.%T.IsEmpty()", a, a.Extra)
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
//nolint:thelper // It's more useful if this test failure shows this line instead of its call site
return a, func(t *testing.T, got *types.StateAccount) {
assert.Truef(t, a.Extra.Equal(nil), "%T.%T.IsEmpty()", a, a.Extra)
}
},
wantTrieHash: vanillaGeth,
},
{
name: "true-boolean payload",
registerAndSetExtra: func(a *types.StateAccount) *types.StateAccount {
types.RegisterExtras[bool]().SetOnPayloadCarrier(a, true)
return a
},
assertExtra: func(t *testing.T, sa *types.StateAccount) {
t.Helper()
assert.Truef(t, types.ExtraPayloads[bool]{}.FromPayloadCarrier(sa), "")
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
e := types.RegisterExtras[bool]()
e.StateAccount.Set(a, true)
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
assert.Truef(t, e.StateAccount.Get(got), "")
}
},
wantTrieHash: trueBool,
},
{
name: "explicit false-boolean payload",
registerAndSetExtra: func(a *types.StateAccount) *types.StateAccount {
p := types.RegisterExtras[bool]()
p.SetOnPayloadCarrier(a, false) // the explicit part
return a
},
assertExtra: func(t *testing.T, sa *types.StateAccount) {
t.Helper()
assert.Falsef(t, types.ExtraPayloads[bool]{}.FromPayloadCarrier(sa), "")
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
e := types.RegisterExtras[bool]()
e.StateAccount.Set(a, false) // the explicit part

return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
assert.Falsef(t, e.StateAccount.Get(got), "")
}
},
wantTrieHash: falseBool,
},
{
name: "implicit false-boolean payload",
registerAndSetExtra: func(a *types.StateAccount) *types.StateAccount {
types.RegisterExtras[bool]()
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
e := types.RegisterExtras[bool]()
// Note that `a` is reflected, unchanged (the implicit part).
return a
},
assertExtra: func(t *testing.T, sa *types.StateAccount) {
t.Helper()
assert.Falsef(t, types.ExtraPayloads[bool]{}.FromPayloadCarrier(sa), "")
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
assert.Falsef(t, e.StateAccount.Get(got), "")
}
},
wantTrieHash: falseBool,
},
{
name: "arbitrary payload",
registerAndSetExtra: func(a *types.StateAccount) *types.StateAccount {
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
e := types.RegisterExtras[arbitraryPayload]()
p := arbitraryPayload{arbitraryData}
types.RegisterExtras[arbitraryPayload]().SetOnPayloadCarrier(a, p)
return a
},
assertExtra: func(t *testing.T, sa *types.StateAccount) {
t.Helper()
got := types.ExtraPayloads[arbitraryPayload]{}.FromPayloadCarrier(sa)
assert.Equalf(t, arbitraryPayload{arbitraryData}, got, "")
e.StateAccount.Set(a, p)
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
assert.Equalf(t, arbitraryPayload{arbitraryData}, e.StateAccount.Get(got), "")
}
},
wantTrieHash: arbitrary,
},
Expand All @@ -127,7 +123,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
types.TestOnlyClearRegisteredExtras()
t.Cleanup(types.TestOnlyClearRegisteredExtras)

acct := tt.registerAndSetExtra(&types.StateAccount{
acct, asserter := tt.registerAndSetExtra(&types.StateAccount{
Nonce: 42,
Balance: uint256.NewInt(314159),
Root: types.EmptyRootHash,
Expand All @@ -147,7 +143,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
if diff := cmp.Diff(acct, got); diff != "" {
t.Errorf("%T.GetAccount() not equal to value passed to %[1]T.UpdateAccount(); diff (-want +got):\n%s", state, diff)
}
tt.assertExtra(t, got)
asserter(t, got)
})
}
}
49 changes: 49 additions & 0 deletions libevm/pseudo/accessor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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
// <http://www.gnu.org/licenses/>.

package pseudo

// An Accessor provides access to T values held in other types.
type Accessor[Container any, T any] struct {
get func(Container) *Type
set func(Container, *Type)
}

// NewAccessor constructs a new [Accessor]. The `get` function MUST return a
// [Type] holding a T.
func NewAccessor[C any, T any](get func(C) *Type, set func(C, *Type)) Accessor[C, T] {
return Accessor[C, T]{get, set}
}

// Get returns the T held by the Container.
func (a Accessor[C, T]) Get(from C) T {
return MustNewValue[T](a.get(from)).Get()
}

// Get returns a pointer to the T held by the Container, which is guaranteed to
// be non-nil. However, if T is itself a pointer, no guarantees are provided.
//
// Note that copying a Container might result in a shallow copy and that the *T
// returned here will therefore be shared by all copies. If this is not the
// desired behaviour, use [Accessor.Set].
func (a Accessor[C, T]) GetPointer(from C) *T {
return MustPointerTo[T](a.get(from)).Value.Get()
}

// Set sets the T carried by the Container.
func (a Accessor[C, T]) Set(on C, val T) {
a.set(on, From(val).Type)
}
63 changes: 17 additions & 46 deletions params/config.libevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,22 @@ func (e *Extras[C, R]) newForRules(c *ChainConfig, r *Rules, blockNum *big.Int,
if e.NewRules == nil {
return registeredExtras.Get().newRules()
}
rExtra := e.NewRules(c, r, e.payloads().FromChainConfig(c), blockNum, isMerge, timestamp)
rExtra := e.NewRules(c, r, e.payloads().ChainConfig.Get(c), blockNum, isMerge, timestamp)
return pseudo.From(rExtra).Type
}

func (*Extras[C, R]) payloads() (g ExtraPayloads[C, R]) { return }
func (*Extras[C, R]) payloads() ExtraPayloads[C, R] {
return ExtraPayloads[C, R]{
ChainConfig: pseudo.NewAccessor[*ChainConfig, C](
(*ChainConfig).extraPayload,
func(c *ChainConfig, t *pseudo.Type) { c.extra = t },
),
Rules: pseudo.NewAccessor[*Rules, R](
(*Rules).extraPayload,
func(r *Rules, t *pseudo.Type) { r.extra = t },
),
}
}

// mustBeStructOrPointerToOne panics if `T` isn't a struct or a *struct.
func mustBeStructOrPointerToOne[T any]() {
Expand Down Expand Up @@ -144,60 +155,20 @@ func notStructMessage[T any]() string {
// [ChainConfig] and [Rules] structs. The only valid way to construct an
// instance is by a call to [RegisterExtras].
type ExtraPayloads[C ChainConfigHooks, R RulesHooks] struct {
_ struct{} // make godoc show unexported fields so nobody tries to make their own instance ;)
}

// FromChainConfig returns the ChainConfig's extra payload.
func (ExtraPayloads[C, R]) FromChainConfig(c *ChainConfig) C {
return pseudo.MustNewValue[C](c.extraPayload()).Get()
}

// PointerFromChainConfig returns a pointer to the ChainConfig's extra payload.
// This is guaranteed to be non-nil.
//
// Note that copying a ChainConfig by dereferencing a pointer will result in a
// shallow copy and that the *C returned here will therefore be shared by all
// copies. If this is not the desired behaviour, use
// [ExtraPayloads.SetOnChainConfig].
func (ExtraPayloads[C, R]) PointerFromChainConfig(c *ChainConfig) *C {
return pseudo.MustPointerTo[C](c.extraPayload()).Value.Get()
}

// SetOnChainConfig sets the ChainConfig's extra payload.
func (e ExtraPayloads[C, R]) SetOnChainConfig(cc *ChainConfig, val C) {
cc.extra = pseudo.From(val).Type
ChainConfig pseudo.Accessor[*ChainConfig, C]
Rules pseudo.Accessor[*Rules, R]
}

// hooksFromChainConfig is equivalent to FromChainConfig(), but returns an
// interface instead of the concrete type implementing it; this allows it to be
// used in non-generic code.
func (e ExtraPayloads[C, R]) hooksFromChainConfig(c *ChainConfig) ChainConfigHooks {
return e.FromChainConfig(c)
}

// FromRules returns the Rules' extra payload.
func (ExtraPayloads[C, R]) FromRules(r *Rules) R {
return pseudo.MustNewValue[R](r.extraPayload()).Get()
}

// PointerFromRules returns a pointer to the Rules's extra payload. This is
// guaranteed to be non-nil.
//
// Note that copying a Rules by dereferencing a pointer will result in a shallow
// copy and that the *R returned here will therefore be shared by all copies. If
// this is not the desired behaviour, use [ExtraPayloads.SetOnRules].
func (ExtraPayloads[C, R]) PointerFromRules(r *Rules) *R {
return pseudo.MustPointerTo[R](r.extraPayload()).Value.Get()
}

// SetOnRules sets the Rules' extra payload.
func (e ExtraPayloads[C, R]) SetOnRules(r *Rules, val R) {
r.extra = pseudo.From(val).Type
return e.ChainConfig.Get(c)
}

// hooksFromRules is the [RulesHooks] equivalent of hooksFromChainConfig().
func (e ExtraPayloads[C, R]) hooksFromRules(r *Rules) RulesHooks {
return e.FromRules(r)
return e.Rules.Get(r)
}

// addRulesExtra is called at the end of [ChainConfig.Rules]; it exists to
Expand Down
Loading
Loading