diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 48bfb021b9..badc9b5d0c 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -194,7 +194,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa if params.BeaconRoot != nil { return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root")) } - switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) { + switch api.eth.BlockChain().Config().LatestPostLondonFork(params.Timestamp) { case forks.Paris: if params.Withdrawals != nil { return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai")) @@ -220,7 +220,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa if params.BeaconRoot == nil { return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root")) } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { + if api.eth.BlockChain().Config().LatestPostLondonFork(params.Timestamp) != forks.Cancun { return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads")) } } @@ -465,7 +465,7 @@ func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.Payl if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun")) } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai { + if api.eth.BlockChain().Config().LatestPostLondonFork(params.Timestamp) == forks.Shanghai { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -502,7 +502,7 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { + if api.eth.BlockChain().Config().LatestPostLondonFork(params.Timestamp) != forks.Cancun { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 must only be called for cancun payloads")) } return api.newPayload(params, versionedHashes, beaconRoot) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1c3cb4adf9..a127f4f2be 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2164,6 +2164,25 @@ func (api *NetAPI) Version() string { return fmt.Sprintf("%d", api.networkVersion) } +// GethAPI offers geth-specific API methods. +type GethAPI struct { + b Backend +} + +// NewGethAPI creates a new net API instance. +func NewGethAPI(b Backend) *GethAPI { + return &GethAPI{b: b} +} + +// Fork returns the latest enabled fork as of the given block. +func (api *GethAPI) Fork(ctx context.Context, num rpc.BlockNumber) (string, error) { + b, err := api.b.BlockByNumber(ctx, num) + if err != nil { + return "", fmt.Errorf("block #%d not found", num) + } + return api.b.ChainConfig().LatestFork(b.Number(), b.Time()).String(), nil +} + // checkTxFee is an internal function used to check whether the fee of // the given transaction is _reasonable_(under the cap). func checkTxFee(gasPrice *big.Int, gas uint64, cap float64) error { diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 2a45ba0921..55cbe6b67c 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -123,6 +123,9 @@ func GetAPIs(apiBackend Backend) []rpc.API { }, { Namespace: "personal", Service: NewPersonalAccountAPI(apiBackend, nonceLock), + }, { + Namespace: "geth", + Service: NewGethAPI(apiBackend), }, } } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 4a1a37d722..a9e3b45f32 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -31,6 +31,7 @@ var Modules = map[string]string{ "les": LESJs, "vflux": VfluxJs, "dev": DevJs, + "geth": GethJs, } const CliqueJs = ` @@ -887,3 +888,17 @@ web3._extend({ ], }); ` + +const GethJs = ` +web3._extend({ + property: 'geth', + methods: + [ + new web3._extend.Method({ + name: 'fork', + call: 'geth_fork', + params: 1 + }), + ], +}); +` diff --git a/params/config.go b/params/config.go index c4e6ba6948..17a57a1ccd 100644 --- a/params/config.go +++ b/params/config.go @@ -533,6 +533,13 @@ func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *bi return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0 } +// IsParis returns whether the given block is either equal to the Paris (merge) +// fork or greater. +// Note: Only usable for post-merge networks where MergeNetsplitBlock has been configured. +func (c *ChainConfig) IsParis(num *big.Int) bool { + return c.IsLondon(num) && isBlockForked(c.MergeNetsplitBlock, num) +} + // IsShanghai returns whether time is either equal to the Shanghai fork time or greater. func (c *ChainConfig) IsShanghai(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.ShanghaiTime, time) @@ -732,8 +739,8 @@ func (c *ChainConfig) ElasticityMultiplier() uint64 { return DefaultElasticityMultiplier } -// LatestFork returns the latest time-based fork that would be active for the given time. -func (c *ChainConfig) LatestFork(time uint64) forks.Fork { +// LatestPostLondonFork returns the latest time-based fork that would be active for the given time. +func (c *ChainConfig) LatestPostLondonFork(time uint64) forks.Fork { // Assume last non-time-based fork has passed. london := c.LondonBlock @@ -749,6 +756,47 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork { } } +func (c *ChainConfig) LatestFork(num *big.Int, time uint64) forks.Fork { + switch { + case c.IsPrague(num, time): + return forks.Prague + case c.IsCancun(num, time): + return forks.Cancun + case c.IsShanghai(num, time): + return forks.Shanghai + case c.IsParis(num): + return forks.Paris + case c.IsGrayGlacier(num): + return forks.GrayGlacier + case c.IsArrowGlacier(num): + return forks.ArrowGlacier + case c.IsLondon(num): + return forks.London + case c.IsBerlin(num): + return forks.Berlin + case c.IsMuirGlacier(num): + return forks.MuirGlacier + case c.IsIstanbul(num): + return forks.Istanbul + case c.IsPetersburg(num): + return forks.Petersburg + case c.IsConstantinople(num): + return forks.Constantinople + case c.IsByzantium(num): + return forks.Byzantium + case c.IsEIP158(num): + return forks.SpuriousDragon + case c.IsEIP155(num): + return forks.TangerineWhistle + case c.IsEIP150(num): + return forks.DAO + case c.IsHomestead(num): + return forks.Homestead + default: + return forks.Frontier + } +} + // isForkBlockIncompatible returns true if a fork scheduled at block s1 cannot be // rescheduled to block s2 because head is already past the fork. func isForkBlockIncompatible(s1, s2, head *big.Int) bool { diff --git a/params/forks/forks.go b/params/forks/forks.go index 4f50ff5aed..659f144a41 100644 --- a/params/forks/forks.go +++ b/params/forks/forks.go @@ -40,3 +40,48 @@ const ( Cancun Prague ) + +func (f Fork) String() string { + switch f { + case Prague: + return "prague" + case Cancun: + return "cancun" + case Shanghai: + return "shanghai" + case Paris: + return "paris" + case GrayGlacier: + return "grayGlacier" + case ArrowGlacier: + return "arrowGlacier" + case London: + return "london" + case Berlin: + return "berlin" + case MuirGlacier: + return "muirGlacier" + case Istanbul: + return "istanbul" + case Petersburg: + return "petersburg" + case Constantinople: + return "constantinople" + case Byzantium: + return "byzantium" + case SpuriousDragon: + return "spuriousDragon" + case TangerineWhistle: + return "tangerineWhistle" + case DAO: + return "dao" + case Homestead: + return "homestead" + case FrontierThawing: + return "frontierThawing" + case Frontier: + return "frontier" + default: + panic("unknown fork") + } +}