From 85558aaca1d82487fca30c376c09ca2fc538d7d0 Mon Sep 17 00:00:00 2001 From: MiniFrenchBread <103425574+MiniFrenchBread@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:31:13 +0800 Subject: [PATCH] feat: restaking --- cmd/evm/internal/t8ntool/execution.go | 12 ++++++++++++ cmd/geth/chaincmd.go | 5 +++++ cmd/geth/config.go | 4 ++++ cmd/geth/main.go | 1 + cmd/utils/flags.go | 5 +++++ consensus/beacon/consensus.go | 1 + core/chain_makers.go | 11 +++++++++++ core/genesis.go | 4 ++++ core/state_processor.go | 21 +++++++++++++++++++++ eth/backend.go | 3 +++ eth/ethconfig/config.go | 7 +++++++ eth/ethconfig/gen_config.go | 6 ++++++ internal/ethapi/simulate.go | 11 +++++++++++ miner/worker.go | 11 +++++++++++ params/config.go | 19 +++++++++++++++++++ 15 files changed, 121 insertions(+) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 5ff5b07a0c..32d31381b8 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -347,6 +347,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // Apply withdrawals for _, w := range pre.Env.Withdrawals { if chainConfig.IsDelegationActive(vmContext.BlockNumber, vmContext.Time) { + // will skip both native staking and restaking reward distribution withdrawals if w.Validator == gomath.MaxUint64 { continue } @@ -378,6 +379,17 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } } } + if chainConfig.IsRestakingActive(vmContext.BlockNumber, vmContext.Time) { + if len(pre.Env.Withdrawals) > 1 { + secondWithdrawal := pre.Env.Withdrawals[1] + if secondWithdrawal.Validator == gomath.MaxUint64 { + amount := new(big.Int).Mul(new(big.Int).SetUint64(secondWithdrawal.Amount), big.NewInt(params.GWei)) + if err := core.ProcessRestakingDistribution(evm, secondWithdrawal.Address, amount); err != nil { + log.Error("could not process restaking distribution", "err", err) + } + } + } + } } // EIP-7002 if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index cf1933151f..5ec4d80695 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -56,6 +56,7 @@ var ( utils.OverridePrague, utils.OverrideVerkle, utils.OverrideDelegationActivation, + utils.OverrideRestakingActivation, }, utils.DatabaseFlags), Description: ` The init command initializes a new genesis block and definition for the network. @@ -241,6 +242,10 @@ func initGenesis(ctx *cli.Context) error { v := ctx.Uint64(utils.OverrideDelegationActivation.Name) overrides.OverrideDelegationActivation = &v } + if ctx.IsSet(utils.OverrideRestakingActivation.Name) { + v := ctx.Uint64(utils.OverrideRestakingActivation.Name) + overrides.OverrideRestakingActivation = &v + } chaindb, err := stack.OpenDatabaseWithFreezer("chaindata", 0, 0, ctx.String(utils.AncientFlag.Name), "", false) if err != nil { diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 5e21540cb8..6bd6975637 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -195,6 +195,10 @@ func makeFullNode(ctx *cli.Context) *node.Node { v := ctx.Uint64(utils.OverrideDelegationActivation.Name) cfg.Eth.OverrideDelegationActivation = &v } + if ctx.IsSet(utils.OverrideRestakingActivation.Name) { + v := ctx.Uint64(utils.OverrideRestakingActivation.Name) + cfg.Eth.OverrideRestakingActivation = &v + } // Start metrics export if enabled utils.SetupMetrics(&cfg.Metrics) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index a7ebd0e322..19418a238a 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -65,6 +65,7 @@ var ( utils.OverridePrague, utils.OverrideVerkle, utils.OverrideDelegationActivation, + utils.OverrideRestakingActivation, utils.EnablePersonal, // deprecated utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 0d837e1947..1acc28419b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -253,6 +253,11 @@ var ( Usage: "Manually specify the Delegation Activation timestamp, overriding the bundled setting", Category: flags.EthCategory, } + OverrideRestakingActivation = &cli.Uint64Flag{ + Name: "override.restakingActivation", + Usage: "Manually specify the Restaking Activation timestamp, overriding the bundled setting", + Category: flags.EthCategory, + } SyncModeFlag = &cli.StringFlag{ Name: "syncmode", Usage: `Blockchain sync mode ("snap" or "full")`, diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index a99c76080c..ce2f24040c 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -346,6 +346,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // Withdrawals processing. for _, w := range body.Withdrawals { if chain.Config().IsDelegationActive(header.Number, header.Time) { + // will skip both native staking and restaking reward distribution withdrawals if w.Validator == math.MaxUint64 { continue } diff --git a/core/chain_makers.go b/core/chain_makers.go index 67d06740d8..7ac8cb4870 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -339,6 +339,17 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) { } } } + if b.cm.config.IsRestakingActive(b.header.Number, b.header.Time) { + if len(b.withdrawals) > 1 { + secondWithdrawal := b.withdrawals[1] + if secondWithdrawal.Validator == math.MaxUint64 { + amount := new(big.Int).Mul(new(big.Int).SetUint64(secondWithdrawal.Amount), big.NewInt(params.GWei)) + if err := ProcessRestakingDistribution(evm, secondWithdrawal.Address, amount); err != nil { + log.Error("could not process restaking distribution", "err", err) + } + } + } + } } // EIP-7002 if err := ProcessWithdrawalQueue(&requests, evm); err != nil { diff --git a/core/genesis.go b/core/genesis.go index 35a62ccc63..eb38e5d58d 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -261,6 +261,7 @@ type ChainOverrides struct { OverridePrague *uint64 OverrideVerkle *uint64 OverrideDelegationActivation *uint64 + OverrideRestakingActivation *uint64 } // apply applies the chain overrides on the supplied chain config. @@ -277,6 +278,9 @@ func (o *ChainOverrides) apply(cfg *params.ChainConfig) error { if o.OverrideDelegationActivation != nil { cfg.DelegationActivationTime = o.OverrideDelegationActivation } + if o.OverrideRestakingActivation != nil { + cfg.RestakingActivationTime = o.OverrideRestakingActivation + } return cfg.CheckConfigForkOrder() } diff --git a/core/state_processor.go b/core/state_processor.go index 0bd6bfeddf..d48f68a1db 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -125,6 +125,17 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } } } + if p.config.IsRestakingActive(block.Number(), block.Time()) { + if len(block.Withdrawals()) > 1 { + secondWithdrawal := block.Withdrawals()[1] + if secondWithdrawal.Validator == math.MaxUint64 { + amount := new(big.Int).Mul(new(big.Int).SetUint64(secondWithdrawal.Amount), big.NewInt(params.GWei)) + if err := ProcessRestakingDistribution(evm, secondWithdrawal.Address, amount); err != nil { + log.Error("could not process restaking distribution", "err", err) + } + } + } + } } // EIP-7002 if err := ProcessWithdrawalQueue(&requests, evm); err != nil { @@ -329,6 +340,16 @@ func ProcessStakingDistribution(evm *vm.EVM, address common.Address, amount *big return nil } +func ProcessRestakingDistribution(evm *vm.EVM, address common.Address, amount *big.Int) error { + evm.StateDB.AddBalance( + address, + uint256.NewInt(0).SetBytes(amount.Bytes()), + tracing.BalanceIncreaseRewardMineBlock, + ) + evm.StateDB.Finalise(true) + return nil +} + func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) error { if tracer := evm.Config.Tracer; tracer != nil { onSystemCallStart(tracer, evm.GetVMContext()) diff --git a/eth/backend.go b/eth/backend.go index 4b5887bd9e..6c9f227541 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -229,6 +229,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if config.OverrideDelegationActivation != nil { overrides.OverrideDelegationActivation = config.OverrideDelegationActivation } + if config.OverrideRestakingActivation != nil { + overrides.OverrideRestakingActivation = config.OverrideRestakingActivation + } eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, &config.TransactionHistory) if err != nil { return nil, err diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index b998d1d50c..3bbcd3c0d7 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -73,6 +73,10 @@ var Defaults = Config{ var i uint64 = 1_749_902_400 // 2025-06-14 12:00:00 UTC return &i }(), + OverrideRestakingActivation: func() *uint64 { + var i uint64 = 1_749_902_400 // TBD + return &i + }(), } //go:generate go run github.com/fjl/gencodec -type Config -formats toml -out gen_config.go @@ -169,6 +173,9 @@ type Config struct { // OverrideDelegationActivation (TODO: remove after the fork) OverrideDelegationActivation *uint64 `toml:",omitempty"` + + // OverrideRestakingActivation (TODO: remove after the fork) + OverrideRestakingActivation *uint64 `toml:",omitempty"` } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 317f1fd277..5d40787e94 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -56,6 +56,7 @@ func (c Config) MarshalTOML() (interface{}, error) { OverridePrague *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` OverrideDelegationActivation *uint64 `toml:",omitempty"` + OverrideRestakingActivation *uint64 `toml:",omitempty"` } var enc Config enc.Genesis = c.Genesis @@ -97,6 +98,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.OverridePrague = c.OverridePrague enc.OverrideVerkle = c.OverrideVerkle enc.OverrideDelegationActivation = c.OverrideDelegationActivation + enc.OverrideRestakingActivation = c.OverrideRestakingActivation return &enc, nil } @@ -142,6 +144,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { OverridePrague *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"` OverrideDelegationActivation *uint64 `toml:",omitempty"` + OverrideRestakingActivation *uint64 `toml:",omitempty"` } var dec Config if err := unmarshal(&dec); err != nil { @@ -264,5 +267,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideDelegationActivation != nil { c.OverrideDelegationActivation = dec.OverrideDelegationActivation } + if dec.OverrideRestakingActivation != nil { + c.OverrideRestakingActivation = dec.OverrideRestakingActivation + } return nil } diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 6dad0ebff4..716e3785c4 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -344,6 +344,17 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, } } } + if sim.chainConfig.IsRestakingActive(header.Number, header.Time) { + if block.BlockOverrides.Withdrawals != nil && len(*block.BlockOverrides.Withdrawals) > 1 { + secondWithdrawal := (*block.BlockOverrides.Withdrawals)[1] + if secondWithdrawal.Validator == math.MaxUint64 { + amount := new(big.Int).Mul(new(big.Int).SetUint64(secondWithdrawal.Amount), big.NewInt(params.GWei)) + if err := core.ProcessRestakingDistribution(evm, secondWithdrawal.Address, amount); err != nil { + log.Error("could not process restaking distribution", "err", err) + } + } + } + } } // EIP-7002 if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { diff --git a/miner/worker.go b/miner/worker.go index e5db9341c6..11c4976c1e 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -138,6 +138,17 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo } } } + if miner.chainConfig.IsRestakingActive(work.header.Number, work.header.Time) { + if len(params.withdrawals) > 1 { + secondWithdrawal := params.withdrawals[1] + if secondWithdrawal.Validator == math.MaxUint64 { + amount := new(big.Int).Mul(new(big.Int).SetUint64(secondWithdrawal.Amount), big.NewInt(ethparams.GWei)) + if err := core.ProcessRestakingDistribution(work.evm, secondWithdrawal.Address, amount); err != nil { + log.Error("could not process restaking distribution", "err", err) + } + } + } + } } // EIP-7002 if err := core.ProcessWithdrawalQueue(&requests, work.evm); err != nil { diff --git a/params/config.go b/params/config.go index 7c55786b77..c6cba5383c 100644 --- a/params/config.go +++ b/params/config.go @@ -65,6 +65,7 @@ var ( Ethash: new(EthashConfig), // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: newUint64(1746612311), + RestakingActivationTime: newUint64(1746612311), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, @@ -97,6 +98,7 @@ var ( Ethash: new(EthashConfig), // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: newUint64(1740434112), + RestakingActivationTime: newUint64(1740434112), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, @@ -129,6 +131,7 @@ var ( Ethash: new(EthashConfig), // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: newUint64(1741159776), + RestakingActivationTime: newUint64(1741159776), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, @@ -161,6 +164,7 @@ var ( Ethash: new(EthashConfig), // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: newUint64(1742999832), + RestakingActivationTime: newUint64(1742999832), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, @@ -196,6 +200,7 @@ var ( Clique: nil, // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: nil, + RestakingActivationTime: nil, } AllDevChainProtocolChanges = &ChainConfig{ @@ -219,6 +224,7 @@ var ( PragueTime: newUint64(0), // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: newUint64(0), + RestakingActivationTime: newUint64(0), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, @@ -255,6 +261,7 @@ var ( Clique: &CliqueConfig{Period: 0, Epoch: 30000}, // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: nil, + RestakingActivationTime: nil, } // TestChainConfig contains every protocol change (EIPs) introduced @@ -287,6 +294,7 @@ var ( Clique: nil, // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: nil, + RestakingActivationTime: nil, } // MergedTestChainConfig contains every protocol change (EIPs) introduced @@ -319,6 +327,7 @@ var ( Clique: nil, // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: newUint64(0), + RestakingActivationTime: newUint64(0), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, @@ -355,6 +364,7 @@ var ( Clique: nil, // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime: nil, + RestakingActivationTime: nil, } TestRules = TestChainConfig.Rules(new(big.Int), false, 0) ) @@ -438,6 +448,8 @@ type ChainConfig struct { DepositContractAddress common.Address `json:"depositContractAddress,omitempty"` // DelegationActivationTime is the activation time of Delegation logic DelegationActivationTime *uint64 `json:"DelegationActivationTime,omitempty"` + // RestakingActivationTime is the activation time of Delegation logic + RestakingActivationTime *uint64 `json:"RestakingActivationTime,omitempty"` // EnableVerkleAtGenesis is a flag that specifies whether the network uses // the Verkle tree starting from the genesis block. If set to true, the @@ -555,6 +567,9 @@ func (c *ChainConfig) Description() string { if c.DelegationActivationTime != nil { banner += fmt.Sprintf(" - DelegationActivationTime: @%-10v\n", *c.DelegationActivationTime) } + if c.RestakingActivationTime != nil { + banner += fmt.Sprintf(" - RestakingActivationTime: @%-10v\n", *c.RestakingActivationTime) + } return banner } @@ -672,6 +687,10 @@ func (c *ChainConfig) IsDelegationActive(num *big.Int, time uint64) bool { return c.IsPrague(num, time) && (c.DelegationActivationTime != nil) && (time >= *c.DelegationActivationTime) } +func (c *ChainConfig) IsRestakingActive(num *big.Int, time uint64) bool { + return c.IsPrague(num, time) && (c.RestakingActivationTime != nil) && (time >= *c.RestakingActivationTime) +} + // IsOsaka returns whether time is either equal to the Osaka fork time or greater. func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time)