From a574ae6ef0eca0e32cc36605ef029ac88577708f Mon Sep 17 00:00:00 2001 From: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Date: Thu, 12 Sep 2024 19:51:00 +0100 Subject: [PATCH] feat: `params.ExtraPayloads.SetOnChainConfig()` + `Rules` equiv (#14) * feat: `params.ExtraPayloadGetter.SetOnChainConfig()` + `Rules` equiv * refactor: rename `ExtraPayloadsGetter` to `ExtraPayloads` --- params/config.libevm.go | 60 +++++++++++++++++++++-------------- params/config.libevm_test.go | 22 ++++++------- params/example.libevm_test.go | 10 +++--- params/hooks.libevm.go | 4 +-- 4 files changed, 54 insertions(+), 42 deletions(-) diff --git a/params/config.libevm.go b/params/config.libevm.go index 2f4730b8aa..e4e43d9122 100644 --- a/params/config.libevm.go +++ b/params/config.libevm.go @@ -47,27 +47,27 @@ type Extras[C ChainConfigHooks, R RulesHooks] struct { // Calls to [ChainConfig.Rules] will call the `NewRules` function of the // registered [Extras] to create a new `R`. // -// The payloads can be accessed via the [ExtraPayloadGetter.FromChainConfig] and -// [ExtraPayloadGetter.FromRules] methods of the getter returned by -// RegisterExtras. Where stated in the interface definitions, they will also be -// used as hooks to alter Ethereum behaviour; if this isn't desired then they -// can embed [NOOPHooks] to satisfy either interface. -func RegisterExtras[C ChainConfigHooks, R RulesHooks](e Extras[C, R]) ExtraPayloadGetter[C, R] { +// The payloads can be accessed via the [ExtraPayloads.FromChainConfig] and +// [ExtraPayloads.FromRules] methods of the accessor returned by RegisterExtras. +// Where stated in the interface definitions, they will also be used as hooks to +// alter Ethereum behaviour; if this isn't desired then they can embed +// [NOOPHooks] to satisfy either interface. +func RegisterExtras[C ChainConfigHooks, R RulesHooks](e Extras[C, R]) ExtraPayloads[C, R] { if registeredExtras != nil { panic("re-registration of Extras") } mustBeStructOrPointerToOne[C]() mustBeStructOrPointerToOne[R]() - getter := e.getter() + payloads := e.payloads() registeredExtras = &extraConstructors{ newChainConfig: pseudo.NewConstructor[C]().Zero, newRules: pseudo.NewConstructor[R]().Zero, reuseJSONRoot: e.ReuseJSONRoot, newForRules: e.newForRules, - getter: getter, + payloads: payloads, } - return getter + return payloads } // TestOnlyClearRegisteredExtras clears the [Extras] previously passed to @@ -102,7 +102,7 @@ type extraConstructors struct { newForRules func(_ *ChainConfig, _ *Rules, blockNum *big.Int, isMerge bool, timestamp uint64) *pseudo.Type // use top-level hooksFrom() functions instead of these as they handle // instances where no [Extras] were registered. - getter interface { + payloads interface { hooksFromChainConfig(*ChainConfig) ChainConfigHooks hooksFromRules(*Rules) RulesHooks } @@ -112,11 +112,11 @@ func (e *Extras[C, R]) newForRules(c *ChainConfig, r *Rules, blockNum *big.Int, if e.NewRules == nil { return registeredExtras.newRules() } - rExtra := e.NewRules(c, r, e.getter().FromChainConfig(c), blockNum, isMerge, timestamp) + rExtra := e.NewRules(c, r, e.payloads().FromChainConfig(c), blockNum, isMerge, timestamp) return pseudo.From(rExtra).Type } -func (*Extras[C, R]) getter() (g ExtraPayloadGetter[C, R]) { return } +func (*Extras[C, R]) payloads() (g ExtraPayloads[C, R]) { return } // mustBeStructOrPointerToOne panics if `T` isn't a struct or a *struct. func mustBeStructOrPointerToOne[T any]() { @@ -140,44 +140,56 @@ func notStructMessage[T any]() string { return fmt.Sprintf("%T is not a struct nor a pointer to a struct", x) } -// An ExtraPayloadGettter provides strongly typed access to the extra payloads -// carried by [ChainConfig] and [Rules] structs. The only valid way to construct -// a getter is by a call to [RegisterExtras]. -type ExtraPayloadGetter[C ChainConfigHooks, R RulesHooks] struct { - _ struct{} // make godoc show unexported fields so nobody tries to make their own getter ;) +// ExtraPayloads provides strongly typed access to the extra payloads carried by +// [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 (ExtraPayloadGetter[C, R]) FromChainConfig(c *ChainConfig) C { +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. -func (ExtraPayloadGetter[C, R]) PointerFromChainConfig(c *ChainConfig) *C { +func (ExtraPayloads[C, R]) PointerFromChainConfig(c *ChainConfig) *C { return pseudo.MustPointerTo[C](c.extraPayload()).Value.Get() } +// SetOnChainConfig sets the ChainConfig's extra payload. It is equivalent to +// `*e.PointerFromChainConfig(cc) = val`. +func (e ExtraPayloads[C, R]) SetOnChainConfig(cc *ChainConfig, val C) { + *e.PointerFromChainConfig(cc) = val +} + // 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 ExtraPayloadGetter[C, R]) hooksFromChainConfig(c *ChainConfig) ChainConfigHooks { +func (e ExtraPayloads[C, R]) hooksFromChainConfig(c *ChainConfig) ChainConfigHooks { return e.FromChainConfig(c) } // FromRules returns the Rules' extra payload. -func (ExtraPayloadGetter[C, R]) FromRules(r *Rules) R { +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. -func (ExtraPayloadGetter[C, R]) PointerFromRules(r *Rules) *R { +func (ExtraPayloads[C, R]) PointerFromRules(r *Rules) *R { return pseudo.MustPointerTo[R](r.extraPayload()).Value.Get() } +// SetOnRules sets the Rules' extra payload. It is equivalent to +// `*e.PointerFromRules(r) = val`. +func (e ExtraPayloads[C, R]) SetOnRules(r *Rules, val R) { + *e.PointerFromRules(r) = val +} + // hooksFromRules is the [RulesHooks] equivalent of hooksFromChainConfig(). -func (e ExtraPayloadGetter[C, R]) hooksFromRules(r *Rules) RulesHooks { +func (e ExtraPayloads[C, R]) hooksFromRules(r *Rules) RulesHooks { return e.FromRules(r) } @@ -195,7 +207,7 @@ func (c *ChainConfig) addRulesExtra(r *Rules, blockNum *big.Int, isMerge bool, t // unmarshalling of JSON), a nil value is constructed and returned. func (c *ChainConfig) extraPayload() *pseudo.Type { if registeredExtras == nil { - // This will only happen if someone constructs an [ExtraPayloadGetter] + // This will only happen if someone constructs an [ExtraPayloads] // directly, without a call to [RegisterExtras]. // // See https://google.github.io/styleguide/go/best-practices#when-to-panic diff --git a/params/config.libevm_test.go b/params/config.libevm_test.go index 129cc76ec5..1e4e309f47 100644 --- a/params/config.libevm_test.go +++ b/params/config.libevm_test.go @@ -135,7 +135,7 @@ func TestModificationOfZeroExtras(t *testing.T) { TestOnlyClearRegisteredExtras() t.Cleanup(TestOnlyClearRegisteredExtras) - getter := RegisterExtras(Extras[ccExtra, rulesExtra]{}) + extras := RegisterExtras(Extras[ccExtra, rulesExtra]{}) config := new(ChainConfig) rules := new(Rules) @@ -143,30 +143,30 @@ func TestModificationOfZeroExtras(t *testing.T) { // closure is demonstrably over the original zero values. assertChainConfigExtra := func(t *testing.T, want ccExtra, msg string) { t.Helper() - assert.Equalf(t, want, getter.FromChainConfig(config), "%T: "+msg, &config) + assert.Equalf(t, want, extras.FromChainConfig(config), "%T: "+msg, &config) } assertRulesExtra := func(t *testing.T, want rulesExtra, msg string) { t.Helper() - assert.Equalf(t, want, getter.FromRules(rules), "%T: "+msg, &rules) + assert.Equalf(t, want, extras.FromRules(rules), "%T: "+msg, &rules) } assertChainConfigExtra(t, ccExtra{}, "zero value") assertRulesExtra(t, rulesExtra{}, "zero value") const answer = 42 - getter.PointerFromChainConfig(config).X = answer + extras.PointerFromChainConfig(config).X = answer assertChainConfigExtra(t, ccExtra{X: answer}, "after setting via pointer field") const pi = 314159 - getter.PointerFromRules(rules).X = pi + extras.PointerFromRules(rules).X = pi assertRulesExtra(t, rulesExtra{X: pi}, "after setting via pointer field") ccReplace := ccExtra{X: 142857} - *getter.PointerFromChainConfig(config) = ccReplace + extras.SetOnChainConfig(config, ccReplace) assertChainConfigExtra(t, ccReplace, "after replacement of entire extra via `*pointer = x`") rulesReplace := rulesExtra{X: 18101986} - *getter.PointerFromRules(rules) = rulesReplace + extras.SetOnRules(rules, rulesReplace) assertRulesExtra(t, rulesReplace, "after replacement of entire extra via `*pointer = x`") if t.Failed() { @@ -177,15 +177,15 @@ func TestModificationOfZeroExtras(t *testing.T) { ccCopy := *config rCopy := *rules - assert.Equal(t, getter.FromChainConfig(&ccCopy), ccReplace, "ChainConfig extras copied") - assert.Equal(t, getter.FromRules(&rCopy), rulesReplace, "Rules extras copied") + assert.Equal(t, extras.FromChainConfig(&ccCopy), ccReplace, "ChainConfig extras copied") + assert.Equal(t, extras.FromRules(&rCopy), rulesReplace, "Rules extras copied") const seqUp = 123456789 - getter.PointerFromChainConfig(&ccCopy).X = seqUp + extras.PointerFromChainConfig(&ccCopy).X = seqUp assertChainConfigExtra(t, ccExtra{X: seqUp}, "original changed because copy only shallow") const seqDown = 987654321 - getter.PointerFromRules(&rCopy).X = seqDown + extras.PointerFromRules(&rCopy).X = seqDown assertRulesExtra(t, rulesExtra{X: seqDown}, "original changed because copy only shallow") }) } diff --git a/params/example.libevm_test.go b/params/example.libevm_test.go index e2a7340d56..b38ea7b9ec 100644 --- a/params/example.libevm_test.go +++ b/params/example.libevm_test.go @@ -30,12 +30,12 @@ func initFn() { // This registration makes *all* [params.ChainConfig] and [params.Rules] // instances respect the payload types. They do not need to be modified to // know about `extraparams`. - getter = params.RegisterExtras(params.Extras[ChainConfigExtra, RulesExtra]{ + payloads = params.RegisterExtras(params.Extras[ChainConfigExtra, RulesExtra]{ NewRules: constructRulesExtra, }) } -var getter params.ExtraPayloadGetter[ChainConfigExtra, RulesExtra] +var payloads params.ExtraPayloads[ChainConfigExtra, RulesExtra] // constructRulesExtra acts as an adjunct to the [params.ChainConfig.Rules] // method. Its primary purpose is to construct the extra payload for the @@ -67,12 +67,12 @@ type RulesExtra struct { // FromChainConfig returns the extra payload carried by the ChainConfig. func FromChainConfig(c *params.ChainConfig) ChainConfigExtra { - return getter.FromChainConfig(c) + return payloads.FromChainConfig(c) } // FromRules returns the extra payload carried by the Rules. func FromRules(r *params.Rules) RulesExtra { - return getter.FromRules(r) + return payloads.FromRules(r) } // myForkPrecompiledContracts is analogous to the vm.PrecompiledContracts @@ -114,7 +114,7 @@ func (r RulesExtra) CanCreateContract(*libevm.AddressContext, libevm.StateReader // This example demonstrates how the rest of this file would be used from a // *different* package. -func ExampleExtraPayloadGetter() { +func ExampleExtraPayloads() { initFn() // Outside of an example this is unnecessary as the function will be a regular init(). const forkTime = 530003640 diff --git a/params/hooks.libevm.go b/params/hooks.libevm.go index c44cd3f1f0..74fd88ccef 100644 --- a/params/hooks.libevm.go +++ b/params/hooks.libevm.go @@ -40,7 +40,7 @@ type RulesAllowlistHooks interface { // none were registered. func (c *ChainConfig) Hooks() ChainConfigHooks { if e := registeredExtras; e != nil { - return e.getter.hooksFromChainConfig(c) + return e.payloads.hooksFromChainConfig(c) } return NOOPHooks{} } @@ -49,7 +49,7 @@ func (c *ChainConfig) Hooks() ChainConfigHooks { // none were registered. func (r *Rules) Hooks() RulesHooks { if e := registeredExtras; e != nil { - return e.getter.hooksFromRules(r) + return e.payloads.hooksFromRules(r) } return NOOPHooks{} }