chore: make libevm/examples/extraparams a params testable example

This commit is contained in:
Arran Schlosberg 2024-08-22 19:47:41 +01:00
parent d9dedd76d4
commit 35c8988d4f
No known key found for this signature in database
GPG key ID: 8A30F7E4344B4EF3
2 changed files with 66 additions and 102 deletions

View file

@ -2,8 +2,6 @@ package params
import ( import (
"encoding/json" "encoding/json"
"fmt"
"log"
"math/big" "math/big"
"testing" "testing"
@ -16,88 +14,6 @@ func testOnlyClearRegisteredExtras() {
registeredExtras = nil registeredExtras = nil
} }
func ExampleRegisterExtras() {
type (
chainConfigExtra struct {
Foo string `json:"foo"`
}
rulesExtra struct {
FooCopy string
}
)
// 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
// modify the Rules but MUST NOT modify the ChainConfig. The value
// that it returns will be available via Rules.ExtraPayload().
return &rulesExtra{
FooCopy: fmt.Sprintf("copy of: %q", cEx.Foo),
}
},
})
defer testOnlyClearRegisteredExtras()
// ChainConfig now unmarshals any JSON field named "extra" into a pointer to
// the registered type, which is available via the ExtraPayload() method.
buf := []byte(`{
"chainId": 1234,
"extra": {
"foo": "hello, world"
}
}`)
config := new(ChainConfig)
if err := json.Unmarshal(buf, config); err != nil {
log.Fatal(err)
}
fmt.Println(config.ChainID)
// The values returned by ExtraPayload() are guaranteed to be pointers to
// 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 := getter.FromChainConfig(config)
rules := config.Rules(nil, false, 0)
rExtra := getter.FromRules(&rules)
if ccExtra != nil {
fmt.Println(ccExtra.Foo)
}
if rExtra != nil {
fmt.Println(rExtra.FooCopy)
}
// Output:
// 1234
// hello, world
// copy of: "hello, world"
}
func ExampleChainConfig_ExtraPayload() {
type (
chainConfigExtra struct{}
rulesExtra struct{}
)
// Typically called in an `init()` function.
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 := getter.FromChainConfig(&c)
// Act on the extra payload...
if extra != nil {
// ...
}
}
type rawJSON struct { type rawJSON struct {
json.RawMessage json.RawMessage
} }
@ -171,6 +87,7 @@ func TestRegisterExtras(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
testOnlyClearRegisteredExtras()
tt.register() tt.register()
defer testOnlyClearRegisteredExtras() defer testOnlyClearRegisteredExtras()
@ -196,6 +113,9 @@ func TestRegisterExtras(t *testing.T) {
} }
func TestExtrasPanic(t *testing.T) { func TestExtrasPanic(t *testing.T) {
testOnlyClearRegisteredExtras()
defer testOnlyClearRegisteredExtras()
assertPanics( assertPanics(
t, func() { t, func() {
RegisterExtras(Extras[int, struct{}]{}) RegisterExtras(Extras[int, struct{}]{})
@ -225,7 +145,6 @@ func TestExtrasPanic(t *testing.T) {
) )
RegisterExtras(Extras[struct{}, struct{}]{}) RegisterExtras(Extras[struct{}, struct{}]{})
defer testOnlyClearRegisteredExtras()
assertPanics( assertPanics(
t, func() { t, func() {

View file

@ -1,32 +1,22 @@
package extraparams package params_test
import ( import (
"encoding/json"
"fmt"
"log"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
var getter params.ExtraPayloadGetter[ChainConfigExtra, RulesExtra] // TODO: explain why this isn't in an init()
func initFn() {
func init() {
getter = params.RegisterExtras(params.Extras[ChainConfigExtra, RulesExtra]{ getter = params.RegisterExtras(params.Extras[ChainConfigExtra, RulesExtra]{
NewForRules: constructRulesExtra, NewForRules: constructRulesExtra,
}) })
} }
type ChainConfigExtra struct { var getter params.ExtraPayloadGetter[ChainConfigExtra, RulesExtra]
MyFeatureTime *uint64
}
type RulesExtra struct {
IsMyFeature bool
}
func constructRulesExtra(c *params.ChainConfig, r *params.Rules, cEx *ChainConfigExtra, blockNum *big.Int, isMerge bool, timestamp uint64) *RulesExtra {
return &RulesExtra{
IsMyFeature: isMerge && cEx.MyFeatureTime != nil && *cEx.MyFeatureTime < timestamp,
}
}
func FromChainConfig(c *params.ChainConfig) *ChainConfigExtra { func FromChainConfig(c *params.ChainConfig) *ChainConfigExtra {
return getter.FromChainConfig(c) return getter.FromChainConfig(c)
@ -35,3 +25,58 @@ func FromChainConfig(c *params.ChainConfig) *ChainConfigExtra {
func FromRules(r *params.Rules) *RulesExtra { func FromRules(r *params.Rules) *RulesExtra {
return getter.FromRules(r) return getter.FromRules(r)
} }
type ChainConfigExtra struct {
MyForkTime *uint64 `json:"myForkTime"`
}
type RulesExtra struct {
IsMyFork bool
}
func constructRulesExtra(c *params.ChainConfig, r *params.Rules, cEx *ChainConfigExtra, blockNum *big.Int, isMerge bool, timestamp uint64) *RulesExtra {
return &RulesExtra{
IsMyFork: cEx.MyForkTime != nil && *cEx.MyForkTime <= timestamp,
}
}
func ExampleRegisterExtras() {
initFn() // TODO: explain
const forkTime = 530003640
jsonData := fmt.Sprintf(`{
"chainId": 1234,
"extra": {
"myForkTime": %d
}
}`, forkTime)
// ChainConfig now unmarshals any JSON field named "extra" into a pointer to
// the registered type, which is available via the ExtraPayload() method.
config := new(params.ChainConfig)
if err := json.Unmarshal([]byte(jsonData), config); err != nil {
log.Fatal(err)
}
fmt.Println(config.ChainID) // original geth fields work as expected
ccExtra := FromChainConfig(config)
if ccExtra != nil && ccExtra.MyForkTime != nil {
fmt.Println(*ccExtra.MyForkTime)
}
for _, time := range []uint64{forkTime - 1, forkTime, forkTime + 1} {
rules := config.Rules(nil, false, time)
rExtra := FromRules(&rules)
if rExtra != nil {
fmt.Printf("%+v\n", rExtra)
}
}
// Output:
// 1234
// 530003640
// &{IsMyFork:false}
// &{IsMyFork:true}
// &{IsMyFork:true}
}