// 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 // . // In practice, everything in this file except for the Example() function SHOULD // be a standalone package, typically called `extraparams`. As long as this new // package is imported anywhere, its init() function will register the "extra" // types, which can be accessed via [extraparams.FromChainConfig] and/or // [extraparams.FromRules]. In all other respects, the [params.ChainConfig] and // [params.Rules] types will act as expected. // // The Example() function demonstrates how the `extraparams` package might be // used from elsewhere. package params_test import ( "encoding/json" "errors" "fmt" "log" "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/libevm" "github.com/ethereum/go-ethereum/params" ) // In practice this would be a regular init() function but nuances around the // testing of this package require it to be called in the Example(). func initFn() { params.TestOnlyClearRegisteredExtras() // not necessary outside of the example // 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`. payloads = params.RegisterExtras(params.Extras[ChainConfigExtra, RulesExtra]{ NewRules: constructRulesExtra, }) } 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 // [params.Rules] but it MAY also modify the [params.Rules]. 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, timestamp: timestamp, } } // ChainConfigExtra can be any struct. Here it just mirrors a common pattern in // the standard [params.ChainConfig] struct. type ChainConfigExtra struct { MyForkTime *uint64 `json:"myForkTime"` // (Optional) If not all hooks are desirable then embedding a [NOOPHooks] // allows the type to satisfy the [ChainConfigHooks] interface, resulting in // default Ethereum behaviour. params.NOOPHooks } // RulesExtra can be any struct. It too mirrors a common pattern in // [params.Rules]. type RulesExtra struct { IsMyFork bool timestamp uint64 params.NOOPHooks } // FromChainConfig returns the extra payload carried by the ChainConfig. func FromChainConfig(c *params.ChainConfig) ChainConfigExtra { return payloads.FromChainConfig(c) } // FromRules returns the extra payload carried by the Rules. func FromRules(r *params.Rules) RulesExtra { return payloads.FromRules(r) } // myForkPrecompiledContracts is analogous to the vm.PrecompiledContracts // maps. Note [RulesExtra.PrecompileOverride] treatment of nil values here. var myForkPrecompiledContracts = map[common.Address]vm.PrecompiledContract{ //... common.BytesToAddress([]byte{0x2}): nil, // i.e disabled //... } // PrecompileOverride implements the required [params.RuleHooks] method. func (r RulesExtra) PrecompileOverride(addr common.Address) (_ libevm.PrecompiledContract, override bool) { if !r.IsMyFork { return nil, false } p, ok := myForkPrecompiledContracts[addr] // The returned boolean indicates whether or not [vm.EVMInterpreter] MUST // override the address, not what it returns as its own `isPrecompile` // boolean. // // Therefore returning `nil, true` here indicates that the precompile will // be disabled. Returning `false` here indicates that the default precompile // behaviour will be exhibited. // // The same pattern can alternatively be implemented with an explicit // `disabledPrecompiles` set to make the behaviour clearer. return p, ok } // CanCreateContract implements the required [params.RuleHooks] method. Access // to state allows it to be configured on-chain however this is an optional // implementation detail. func (r RulesExtra) CanCreateContract(_ *libevm.AddressContext, gas uint64, _ libevm.StateReader) (uint64, error) { if time.Unix(int64(r.timestamp), 0).UTC().Day() != int(time.Tuesday) { //nolint:gosec // G115 timestamp won't overflow int64 for millions of years so this is someone else's problem // Consumes all remaining gas. return 0, errors.New("uh oh") } return gas, nil } // This example demonstrates how the rest of this file would be used from a // *different* package. func ExampleExtraPayloads() { initFn() // Outside of an example this is unnecessary as the function will be a regular init(). const forkTime = 530003640 jsonData := fmt.Sprintf(`{ "chainId": 1234, "extra": { "myForkTime": %d } }`, forkTime) // Because [params.RegisterExtras] has been called, unmarshalling a JSON // field of "extra" into a [params.ChainConfig] will populate a new value of // the registered type. This can be accessed with the [FromChainConfig] // function. config := new(params.ChainConfig) if err := json.Unmarshal([]byte(jsonData), config); err != nil { log.Fatal(err) } fmt.Println("Chain ID", config.ChainID) // original geth fields work as expected ccExtra := FromChainConfig(config) // extraparams.FromChainConfig() in practice if ccExtra.MyForkTime != nil { fmt.Println("Fork time", *ccExtra.MyForkTime) } for _, time := range []uint64{forkTime - 1, forkTime, forkTime + 1} { rules := config.Rules(nil, false, time) rExtra := FromRules(&rules) // extraparams.FromRules() in practice fmt.Printf("IsMyFork at %v: %t\n", rExtra.timestamp, rExtra.IsMyFork) } // Output: // Chain ID 1234 // Fork time 530003640 // IsMyFork at 530003639: false // IsMyFork at 530003640: true // IsMyFork at 530003641: true }