From 0bd2bfc6673ba81bf11604c209b45fb65ce17a1a Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Thu, 22 Aug 2024 17:40:15 +0100 Subject: [PATCH] feat: `params.ExtraPayloadGetter` for end-user type safety --- libevm/examples/extraparams/extraparams.go | 9 +++++---- libevm/examples/go.mod | 2 +- libevm/examples/go.sum | 4 ++-- params/config.libevm.go | 16 +++++++++++++++- params/config.libevm_test.go | 17 +++++++++-------- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/libevm/examples/extraparams/extraparams.go b/libevm/examples/extraparams/extraparams.go index fd240a0d40..0ca03cd3b6 100644 --- a/libevm/examples/extraparams/extraparams.go +++ b/libevm/examples/extraparams/extraparams.go @@ -3,12 +3,13 @@ package extraparams import ( "math/big" - "github.com/ethereum/go-ethereum/libevm/pseudo" "github.com/ethereum/go-ethereum/params" ) +var getter params.ExtraPayloadGetter[ChainConfigExtra, RulesExtra] + func init() { - params.RegisterExtras(params.Extras[ChainConfigExtra, RulesExtra]{ + getter = params.RegisterExtras(params.Extras[ChainConfigExtra, RulesExtra]{ NewForRules: constructRulesExtra, }) } @@ -28,9 +29,9 @@ func constructRulesExtra(c *params.ChainConfig, r *params.Rules, cEx *ChainConfi } func FromChainConfig(c *params.ChainConfig) *ChainConfigExtra { - return pseudo.NewValueUnsafe[*ChainConfigExtra](c.ExtraPayload()).Get() + return getter.FromChainConfig(c) } func FromRules(r *params.Rules) *RulesExtra { - return pseudo.NewValueUnsafe[*RulesExtra](r.ExtraPayload()).Get() + return getter.FromRules(r) } diff --git a/libevm/examples/go.mod b/libevm/examples/go.mod index 22735c18a9..84e13d9b63 100644 --- a/libevm/examples/go.mod +++ b/libevm/examples/go.mod @@ -9,5 +9,5 @@ require github.com/ethereum/go-ethereum v0.0.0-00010101000000-000000000000 require ( github.com/holiman/uint256 v1.3.1 // indirect golang.org/x/crypto v0.22.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sys v0.22.0 // indirect ) diff --git a/libevm/examples/go.sum b/libevm/examples/go.sum index 29f549c59f..145acea3bb 100644 --- a/libevm/examples/go.sum +++ b/libevm/examples/go.sum @@ -8,7 +8,7 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/params/config.libevm.go b/params/config.libevm.go index 884a5f2574..8fe40e709d 100644 --- a/params/config.libevm.go +++ b/params/config.libevm.go @@ -41,13 +41,27 @@ type Extras[C any, R any] struct { // result in a typed-nil bug. This pattern most-closely resembles a fully // generic implementation and users SHOULD wrap the type assertions in a shared // package. -func RegisterExtras[C any, R any](e Extras[C, R]) { +func RegisterExtras[C any, R any](e Extras[C, R]) ExtraPayloadGetter[C, R] { if registeredExtras != nil { panic("re-registration of Extras") } mustBeStruct[C]() mustBeStruct[R]() registeredExtras = &e + return ExtraPayloadGetter[C, R]{} +} + +// An ExtraPayloadGettter ... +type ExtraPayloadGetter[C any, R any] struct{} + +// FromChainConfig ... +func (ExtraPayloadGetter[C, R]) FromChainConfig(c *ChainConfig) *C { + return pseudo.NewValueUnsafe[*C](c.ExtraPayload()).Get() +} + +// FromRules ... +func (ExtraPayloadGetter[C, R]) FromRules(r *Rules) *R { + return pseudo.NewValueUnsafe[*R](r.ExtraPayload()).Get() } func mustBeStruct[T any]() { diff --git a/params/config.libevm_test.go b/params/config.libevm_test.go index 18824f1457..26f14d2cef 100644 --- a/params/config.libevm_test.go +++ b/params/config.libevm_test.go @@ -26,8 +26,9 @@ func ExampleRegisterExtras() { } ) - // In practice, this would be called inside an init() func. - RegisterExtras(Extras[chainConfigExtra, rulesExtra]{ + // In practice, this would be called inside an init() func and the `getter` + // used to access the ExtraPayload() values in a type-safe way. + getter := RegisterExtras(Extras[chainConfigExtra, rulesExtra]{ NewForRules: func(cc *ChainConfig, r *Rules, cEx *chainConfigExtra, blockNum *big.Int, isMerge bool, timestamp uint64) *rulesExtra { // This function is called at the end of ChainConfig.Rules(), // receiving a pointer to the Rules that will be returned. It MAY @@ -49,8 +50,8 @@ func ExampleRegisterExtras() { } }`) - var config ChainConfig - if err := json.Unmarshal(buf, &config); err != nil { + config := new(ChainConfig) + if err := json.Unmarshal(buf, config); err != nil { log.Fatal(err) } @@ -59,9 +60,9 @@ func ExampleRegisterExtras() { // the registered types. They MAY, however, be nil pointers. In practice, // callers SHOULD abstract the type assertion in a reusable function to // provide a seamless devex. - ccExtra := config.ExtraPayload().Interface().(*chainConfigExtra) + ccExtra := getter.FromChainConfig(config) rules := config.Rules(nil, false, 0) - rExtra := rules.ExtraPayload().Interface().(*rulesExtra) + rExtra := getter.FromRules(&rules) if ccExtra != nil { fmt.Println(ccExtra.Foo) @@ -82,14 +83,14 @@ func ExampleChainConfig_ExtraPayload() { rulesExtra struct{} ) // Typically called in an `init()` function. - RegisterExtras(Extras[chainConfigExtra, rulesExtra]{ /*...*/ }) + getter := RegisterExtras(Extras[chainConfigExtra, rulesExtra]{ /*...*/ }) defer testOnlyClearRegisteredExtras() var c ChainConfig // Sourced from elsewhere, typically unmarshalled from JSON. // Both ChainConfig.ExtraPayload() and Rules.ExtraPayload() return `any` // that are guaranteed to be pointers to the registered types. - extra := c.ExtraPayload().Interface().(*chainConfigExtra) + extra := getter.FromChainConfig(&c) // Act on the extra payload... if extra != nil {