mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
all: implement eip-7002 EL triggered withdrawal requests
This commit is contained in:
parent
c3f13b2a1c
commit
cc3e859d41
12 changed files with 463 additions and 128 deletions
|
|
@ -35,6 +35,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
Deposits types.Deposits `json:"depositRequests"`
|
Deposits types.Deposits `json:"depositRequests"`
|
||||||
|
WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests"`
|
||||||
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
var enc ExecutableData
|
var enc ExecutableData
|
||||||
|
|
@ -61,6 +62,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
||||||
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
||||||
enc.Deposits = e.Deposits
|
enc.Deposits = e.Deposits
|
||||||
|
enc.WithdrawalRequests = e.WithdrawalRequests
|
||||||
enc.ExecutionWitness = e.ExecutionWitness
|
enc.ExecutionWitness = e.ExecutionWitness
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
@ -86,6 +88,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
Deposits *types.Deposits `json:"depositRequests"`
|
Deposits *types.Deposits `json:"depositRequests"`
|
||||||
|
WithdrawalRequests *types.WithdrawalRequests `json:"withdrawalRequests"`
|
||||||
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
var dec ExecutableData
|
var dec ExecutableData
|
||||||
|
|
@ -163,6 +166,9 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
if dec.Deposits != nil {
|
if dec.Deposits != nil {
|
||||||
e.Deposits = *dec.Deposits
|
e.Deposits = *dec.Deposits
|
||||||
}
|
}
|
||||||
|
if dec.WithdrawalRequests != nil {
|
||||||
|
e.WithdrawalRequests = *dec.WithdrawalRequests
|
||||||
|
}
|
||||||
if dec.ExecutionWitness != nil {
|
if dec.ExecutionWitness != nil {
|
||||||
e.ExecutionWitness = dec.ExecutionWitness
|
e.ExecutionWitness = dec.ExecutionWitness
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ type ExecutableData struct {
|
||||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||||
Deposits types.Deposits `json:"depositRequests"`
|
Deposits types.Deposits `json:"depositRequests"`
|
||||||
|
WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests"`
|
||||||
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +234,9 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
|
||||||
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
|
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
|
||||||
withdrawalsRoot = &h
|
withdrawalsRoot = &h
|
||||||
}
|
}
|
||||||
// Compute requestsHash if any requests are non-nil.
|
// Only set requestsHash if there exists requests in the ExecutableData. This
|
||||||
|
// allows CLs to continue using the data structure before requests are
|
||||||
|
// enabled.
|
||||||
var (
|
var (
|
||||||
requestsHash *common.Hash
|
requestsHash *common.Hash
|
||||||
requests types.Requests
|
requests types.Requests
|
||||||
|
|
@ -243,9 +246,17 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
|
||||||
for _, d := range data.Deposits {
|
for _, d := range data.Deposits {
|
||||||
requests = append(requests, types.NewRequest(d))
|
requests = append(requests, types.NewRequest(d))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if data.WithdrawalRequests != nil {
|
||||||
|
for _, w := range data.WithdrawalRequests {
|
||||||
|
requests = append(requests, types.NewRequest(w))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if requests != nil {
|
||||||
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
||||||
requestsHash = &h
|
requestsHash = &h
|
||||||
}
|
}
|
||||||
|
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: data.ParentHash,
|
ParentHash: data.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
|
@ -320,13 +331,17 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
||||||
// assigns them to the associated fields in ExecutableData.
|
// assigns them to the associated fields in ExecutableData.
|
||||||
func setRequests(requests types.Requests, data *ExecutableData) {
|
func setRequests(requests types.Requests, data *ExecutableData) {
|
||||||
if requests != nil {
|
if requests != nil {
|
||||||
// If requests is non-nil, it means deposits are available in block and we
|
// If requests is non-nil, it means the requests are available in block and
|
||||||
// should return an empty slice instead of nil if there are no deposits.
|
// we should return an empty slice instead of nil.
|
||||||
data.Deposits = make(types.Deposits, 0)
|
data.Deposits = make(types.Deposits, 0)
|
||||||
|
data.WithdrawalRequests = make(types.WithdrawalRequests, 0)
|
||||||
}
|
}
|
||||||
for _, r := range requests {
|
for _, r := range requests {
|
||||||
if d, ok := r.Inner().(*types.Deposit); ok {
|
switch v := r.Inner().(type) {
|
||||||
data.Deposits = append(data.Deposits, d)
|
case *types.Deposit:
|
||||||
|
data.Deposits = append(data.Deposits, v)
|
||||||
|
case *types.WithdrawalRequest:
|
||||||
|
data.WithdrawalRequests = append(data.WithdrawalRequests, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -336,6 +351,7 @@ type ExecutionPayloadBody struct {
|
||||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
Deposits types.Deposits `json:"depositRequests"`
|
Deposits types.Deposits `json:"depositRequests"`
|
||||||
|
WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client identifiers to support ClientVersionV1.
|
// Client identifiers to support ClientVersionV1.
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ type ExecutionResult struct {
|
||||||
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
|
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
|
||||||
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"`
|
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"`
|
||||||
RequestsHash *common.Hash `json:"requestsRoot,omitempty"`
|
RequestsHash *common.Hash `json:"requestsRoot,omitempty"`
|
||||||
DepositRequests *types.Deposits `json:"depositRequests,omitempty"`
|
DepositRequests types.Deposits `json:"depositRequests,omitempty"`
|
||||||
|
WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ommer struct {
|
type ommer struct {
|
||||||
|
|
@ -354,6 +355,41 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
|
amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
|
||||||
statedb.AddBalance(w.Address, uint256.MustFromBig(amount), tracing.BalanceIncreaseWithdrawal)
|
statedb.AddBalance(w.Address, uint256.MustFromBig(amount), tracing.BalanceIncreaseWithdrawal)
|
||||||
}
|
}
|
||||||
|
// Retrieve deposit and withdrawal requests
|
||||||
|
var (
|
||||||
|
depositRequests types.Deposits
|
||||||
|
withdrawalRequests types.WithdrawalRequests
|
||||||
|
requestsHash *common.Hash
|
||||||
|
)
|
||||||
|
if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) {
|
||||||
|
// Parse deposit requests from the logs
|
||||||
|
var allLogs []*types.Log
|
||||||
|
for _, receipt := range receipts {
|
||||||
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
|
}
|
||||||
|
requests, err := core.ParseDepositLogs(allLogs, chainConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
||||||
|
}
|
||||||
|
// Process the withdrawal requests contract execution
|
||||||
|
vmenv := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||||
|
wxs := core.ProcessDequeueWithdrawalRequests(vmenv, statedb)
|
||||||
|
requests = append(requests, wxs...)
|
||||||
|
// Calculate the requests root
|
||||||
|
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
||||||
|
requestsHash = &h
|
||||||
|
// Get the deposits from the requests
|
||||||
|
depositRequests = make(types.Deposits, 0)
|
||||||
|
withdrawalRequests = make(types.WithdrawalRequests, 0)
|
||||||
|
for _, req := range requests {
|
||||||
|
switch v := req.Inner().(type) {
|
||||||
|
case *types.Deposit:
|
||||||
|
depositRequests = append(depositRequests, v)
|
||||||
|
case *types.WithdrawalRequest:
|
||||||
|
withdrawalRequests = append(withdrawalRequests, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Commit block
|
// Commit block
|
||||||
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -370,6 +406,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
|
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
|
||||||
GasUsed: (math.HexOrDecimal64)(gasUsed),
|
GasUsed: (math.HexOrDecimal64)(gasUsed),
|
||||||
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
|
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
|
||||||
|
RequestsHash: requestsHash,
|
||||||
|
DepositRequests: depositRequests,
|
||||||
|
WithdrawalRequests: withdrawalRequests,
|
||||||
}
|
}
|
||||||
if pre.Env.Withdrawals != nil {
|
if pre.Env.Withdrawals != nil {
|
||||||
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
|
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
|
||||||
|
|
@ -379,28 +418,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas)
|
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas)
|
||||||
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
|
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
|
||||||
}
|
}
|
||||||
if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) {
|
|
||||||
// Parse the requests from the logs
|
|
||||||
var allLogs []*types.Log
|
|
||||||
for _, receipt := range receipts {
|
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
|
||||||
}
|
|
||||||
requests, err := core.ParseDepositLogs(allLogs, chainConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
|
||||||
}
|
|
||||||
// Calculate the requests root
|
|
||||||
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
|
||||||
execRs.RequestsHash = &h
|
|
||||||
// Get the deposits from the requests
|
|
||||||
deposits := make(types.Deposits, 0)
|
|
||||||
for _, req := range requests {
|
|
||||||
if dep, ok := req.Inner().(*types.Deposit); ok {
|
|
||||||
deposits = append(deposits, dep)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
execRs.DepositRequests = &deposits
|
|
||||||
}
|
|
||||||
// Re-create statedb instance with new root upon the updated database
|
// Re-create statedb instance with new root upon the updated database
|
||||||
// for accessing latest states.
|
// for accessing latest states.
|
||||||
statedb, err = state.New(root, statedb.Database(), nil)
|
statedb, err = state.New(root, statedb.Database(), nil)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -4314,3 +4315,101 @@ func TestEIP6110(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEIP7002 verifies that withdrawal requests are processed correctly in the
|
||||||
|
// pre-deploy and parsed out correctly via the system call.
|
||||||
|
func TestEIP7002(t *testing.T) {
|
||||||
|
var (
|
||||||
|
engine = beacon.NewFaker()
|
||||||
|
|
||||||
|
// A sender who makes transactions, has some funds
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||||
|
config = *params.AllEthashProtocolChanges
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: &config,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
addr: {Balance: funds},
|
||||||
|
params.WithdrawalRequestsAddress: {Code: common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b366038141561012e5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012e57600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f548061049d141561010757505f5b60015460028282011161011c5750505f610122565b01600290035b5f555f600155604c025ff35b5f5ffd")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
gspec.Config.BerlinBlock = common.Big0
|
||||||
|
gspec.Config.LondonBlock = common.Big0
|
||||||
|
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||||
|
gspec.Config.TerminalTotalDifficultyPassed = true
|
||||||
|
gspec.Config.ShanghaiTime = u64(0)
|
||||||
|
gspec.Config.CancunTime = u64(0)
|
||||||
|
gspec.Config.PragueTime = u64(0)
|
||||||
|
signer := types.LatestSigner(gspec.Config)
|
||||||
|
|
||||||
|
// Withdrawal requests to send.
|
||||||
|
wxs := types.WithdrawalRequests{
|
||||||
|
{
|
||||||
|
Source: addr,
|
||||||
|
PublicKey: [48]byte{42},
|
||||||
|
Amount: 42,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: addr,
|
||||||
|
PublicKey: [48]byte{13, 37},
|
||||||
|
Amount: 1337,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||||
|
for i, wx := range wxs {
|
||||||
|
data := make([]byte, 56)
|
||||||
|
copy(data, wx.PublicKey[:])
|
||||||
|
binary.BigEndian.PutUint64(data[48:], wx.Amount)
|
||||||
|
txdata := &types.DynamicFeeTx{
|
||||||
|
ChainID: gspec.Config.ChainID,
|
||||||
|
Nonce: uint64(i),
|
||||||
|
To: ¶ms.WithdrawalRequestsAddress,
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Gas: 500000,
|
||||||
|
GasFeeCap: newGwei(5),
|
||||||
|
GasTipCap: big.NewInt(2),
|
||||||
|
AccessList: nil,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
tx := types.NewTx(txdata)
|
||||||
|
tx, _ = types.SignTx(tx, signer, key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
defer chain.Stop()
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
block := chain.GetBlockByNumber(1)
|
||||||
|
if block == nil {
|
||||||
|
t.Fatalf("failed to retrieve block 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the withdrawal requests match.
|
||||||
|
got := block.Requests()
|
||||||
|
if len(got) != 2 {
|
||||||
|
t.Fatalf("wrong number of withdrawal requests: wanted 2, got %d", len(wxs))
|
||||||
|
}
|
||||||
|
for i, want := range wxs {
|
||||||
|
got, ok := got[i].Inner().(*types.WithdrawalRequest)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected withdrawal request")
|
||||||
|
}
|
||||||
|
if want.Source != got.Source {
|
||||||
|
t.Fatalf("wrong source address: want %s, got %s", want.Source, got.Source)
|
||||||
|
}
|
||||||
|
if want.PublicKey != got.PublicKey {
|
||||||
|
t.Fatalf("wrong public key: want %s, got %s", common.Bytes2Hex(want.PublicKey[:]), common.Bytes2Hex(got.PublicKey[:]))
|
||||||
|
}
|
||||||
|
if want.Amount != got.Amount {
|
||||||
|
t.Fatalf("wrong amount: want %d, got %d", want.Amount, got.Amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -355,6 +355,13 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
}
|
}
|
||||||
requests = append(requests, d...)
|
requests = append(requests, d...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||||
|
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{})
|
||||||
|
)
|
||||||
|
wxs := ProcessDequeueWithdrawalRequests(vmenv, statedb)
|
||||||
|
requests = append(requests, wxs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals, Requests: requests}
|
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals, Requests: requests}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
|
@ -103,6 +104,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
wxs := ProcessDequeueWithdrawalRequests(vmenv, statedb)
|
||||||
|
requests = append(requests, wxs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
|
|
@ -270,3 +273,40 @@ func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) (types.Requ
|
||||||
}
|
}
|
||||||
return deposits, nil
|
return deposits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessDequeueWithdrawalRequests applies the EIP-7002 system call to the withdrawal requests contract.
|
||||||
|
func ProcessDequeueWithdrawalRequests(vmenv *vm.EVM, statedb *state.StateDB) types.Requests {
|
||||||
|
if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallStart != nil {
|
||||||
|
vmenv.Config.Tracer.OnSystemCallStart()
|
||||||
|
}
|
||||||
|
if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallEnd != nil {
|
||||||
|
defer vmenv.Config.Tracer.OnSystemCallEnd()
|
||||||
|
}
|
||||||
|
msg := &Message{
|
||||||
|
From: params.SystemAddress,
|
||||||
|
GasLimit: 30_000_000,
|
||||||
|
GasPrice: common.Big0,
|
||||||
|
GasFeeCap: common.Big0,
|
||||||
|
GasTipCap: common.Big0,
|
||||||
|
To: ¶ms.WithdrawalRequestsAddress,
|
||||||
|
}
|
||||||
|
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
||||||
|
statedb.AddAddressToAccessList(params.WithdrawalRequestsAddress)
|
||||||
|
ret, _, _ := vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||||
|
statedb.Finalise(true)
|
||||||
|
|
||||||
|
// Parse out the exits.
|
||||||
|
var reqs types.Requests
|
||||||
|
for i := 0; i < len(ret)/76; i++ {
|
||||||
|
start := i * 76
|
||||||
|
var pubkey [48]byte
|
||||||
|
copy(pubkey[:], ret[start+20:start+68])
|
||||||
|
wx := &types.WithdrawalRequest{
|
||||||
|
Source: common.BytesToAddress(ret[start : start+20]),
|
||||||
|
PublicKey: pubkey,
|
||||||
|
Amount: binary.BigEndian.Uint64(ret[start+68:]),
|
||||||
|
}
|
||||||
|
reqs = append(reqs, types.NewRequest(wx))
|
||||||
|
}
|
||||||
|
return reqs
|
||||||
|
}
|
||||||
|
|
|
||||||
53
core/types/gen_withdrawal_request_json.go
Normal file
53
core/types/gen_withdrawal_request_json.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*withdrawalRequestMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (w WithdrawalRequest) MarshalJSON() ([]byte, error) {
|
||||||
|
type WithdrawalRequest struct {
|
||||||
|
Source common.Address `json:"sourceAddress"`
|
||||||
|
PublicKey hexutil.Bytes `json:"validatorPubkey"`
|
||||||
|
Amount hexutil.Uint64 `json:"amount"`
|
||||||
|
}
|
||||||
|
var enc WithdrawalRequest
|
||||||
|
enc.Source = w.Source
|
||||||
|
enc.PublicKey = w.PublicKey[:]
|
||||||
|
enc.Amount = hexutil.Uint64(w.Amount)
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (w *WithdrawalRequest) UnmarshalJSON(input []byte) error {
|
||||||
|
type WithdrawalRequest struct {
|
||||||
|
Source *common.Address `json:"sourceAddress"`
|
||||||
|
PublicKey *hexutil.Bytes `json:"validatorPubkey"`
|
||||||
|
Amount *hexutil.Uint64 `json:"amount"`
|
||||||
|
}
|
||||||
|
var dec WithdrawalRequest
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.Source != nil {
|
||||||
|
w.Source = *dec.Source
|
||||||
|
}
|
||||||
|
if dec.PublicKey != nil {
|
||||||
|
if len(*dec.PublicKey) != len(w.PublicKey) {
|
||||||
|
return errors.New("field 'validatorPubkey' has wrong length, need 48 items")
|
||||||
|
}
|
||||||
|
copy(w.PublicKey[:], *dec.PublicKey)
|
||||||
|
}
|
||||||
|
if dec.Amount != nil {
|
||||||
|
w.Amount = uint64(*dec.Amount)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,7 @@ var (
|
||||||
// Request types.
|
// Request types.
|
||||||
const (
|
const (
|
||||||
DepositRequestType = 0x00
|
DepositRequestType = 0x00
|
||||||
|
WithdrawalRequestType = 0x01
|
||||||
)
|
)
|
||||||
|
|
||||||
// Request is an EIP-7685 request object. It represents execution layer
|
// Request is an EIP-7685 request object. It represents execution layer
|
||||||
|
|
@ -149,6 +150,8 @@ func (r *Request) decode(b []byte) (RequestData, error) {
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case DepositRequestType:
|
case DepositRequestType:
|
||||||
inner = new(Deposit)
|
inner = new(Deposit)
|
||||||
|
case WithdrawalRequestType:
|
||||||
|
inner = new(WithdrawalRequest)
|
||||||
default:
|
default:
|
||||||
return nil, ErrRequestTypeNotSupported
|
return nil, ErrRequestTypeNotSupported
|
||||||
}
|
}
|
||||||
|
|
|
||||||
80
core/types/withdrawal_request.go
Normal file
80
core/types/withdrawal_request.go
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type WithdrawalRequest -field-override withdrawalRequestMarshaling -out gen_withdrawal_request_json.go
|
||||||
|
|
||||||
|
// WithdrawalRequest represents an EIP-7002 withdrawal request from source for
|
||||||
|
// the validator associated with the public key for amount.
|
||||||
|
type WithdrawalRequest struct {
|
||||||
|
Source common.Address `json:"sourceAddress"`
|
||||||
|
PublicKey [48]byte `json:"validatorPubkey"`
|
||||||
|
Amount uint64 `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// field type overrides for gencodec
|
||||||
|
type withdrawalRequestMarshaling struct {
|
||||||
|
PublicKey hexutil.Bytes
|
||||||
|
Amount hexutil.Uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithdrawalRequest) Bytes() []byte {
|
||||||
|
out := make([]byte, 76)
|
||||||
|
copy(out, w.Source.Bytes())
|
||||||
|
copy(out[20:], w.PublicKey[:])
|
||||||
|
binary.LittleEndian.PutUint64(out, w.Amount)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithdrawalRequests implements DerivableList for withdrawal requests.
|
||||||
|
type WithdrawalRequests []*WithdrawalRequest
|
||||||
|
|
||||||
|
// Len returns the length of s.
|
||||||
|
func (s WithdrawalRequests) Len() int { return len(s) }
|
||||||
|
|
||||||
|
// EncodeIndex encodes the i'th withdrawal request to w.
|
||||||
|
func (s WithdrawalRequests) EncodeIndex(i int, w *bytes.Buffer) {
|
||||||
|
rlp.Encode(w, s[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requests creates a deep copy of each deposit and returns a slice of the
|
||||||
|
// withdrwawal requests as Request objects.
|
||||||
|
func (s WithdrawalRequests) Requests() (reqs Requests) {
|
||||||
|
for _, d := range s {
|
||||||
|
reqs = append(reqs, NewRequest(d))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithdrawalRequest) requestType() byte { return WithdrawalRequestType }
|
||||||
|
func (w *WithdrawalRequest) encode(b *bytes.Buffer) error { return rlp.Encode(b, w) }
|
||||||
|
func (w *WithdrawalRequest) decode(input []byte) error { return rlp.DecodeBytes(input, w) }
|
||||||
|
func (w *WithdrawalRequest) copy() RequestData {
|
||||||
|
return &WithdrawalRequest{
|
||||||
|
Source: w.Source,
|
||||||
|
PublicKey: w.PublicKey,
|
||||||
|
Amount: w.Amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -872,13 +872,14 @@ func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engin
|
||||||
for i, hash := range hashes {
|
for i, hash := range hashes {
|
||||||
block := api.eth.BlockChain().GetBlockByHash(hash)
|
block := api.eth.BlockChain().GetBlockByHash(hash)
|
||||||
body := getBody(block)
|
body := getBody(block)
|
||||||
if body != nil {
|
|
||||||
// Nil out the V2 values, clients should know to not request V1 objects
|
// Nil out the V2 values, clients should know to not request V1 objects
|
||||||
// after Prague.
|
// after Prague.
|
||||||
|
if body != nil {
|
||||||
body.Deposits = nil
|
body.Deposits = nil
|
||||||
}
|
bodies[i].WithdrawalRequests = nil
|
||||||
bodies[i] = body
|
bodies[i] = body
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return bodies
|
return bodies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -905,6 +906,7 @@ func (api *ConsensusAPI) GetPayloadBodiesByRangeV1(start, count hexutil.Uint64)
|
||||||
for i := range bodies {
|
for i := range bodies {
|
||||||
if bodies[i] != nil {
|
if bodies[i] != nil {
|
||||||
bodies[i].Deposits = nil
|
bodies[i].Deposits = nil
|
||||||
|
bodies[i].WithdrawalRequests = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bodies, nil
|
return bodies, nil
|
||||||
|
|
@ -947,6 +949,7 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody {
|
||||||
txs = make([]hexutil.Bytes, len(body.Transactions))
|
txs = make([]hexutil.Bytes, len(body.Transactions))
|
||||||
withdrawals = body.Withdrawals
|
withdrawals = body.Withdrawals
|
||||||
depositRequests types.Deposits
|
depositRequests types.Deposits
|
||||||
|
withdrawalRequests types.WithdrawalRequests
|
||||||
)
|
)
|
||||||
|
|
||||||
for j, tx := range body.Transactions {
|
for j, tx := range body.Transactions {
|
||||||
|
|
@ -963,15 +966,18 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody {
|
||||||
// type has activated yet or if there are just no requests of that type from
|
// type has activated yet or if there are just no requests of that type from
|
||||||
// only the block.
|
// only the block.
|
||||||
for _, req := range block.Requests() {
|
for _, req := range block.Requests() {
|
||||||
if d, ok := req.Inner().(*types.Deposit); ok {
|
switch v := req.Inner().(type) {
|
||||||
depositRequests = append(depositRequests, d)
|
case *types.Deposit:
|
||||||
|
depositRequests = append(depositRequests, v)
|
||||||
|
case *types.WithdrawalRequest:
|
||||||
|
withdrawalRequests = append(withdrawalRequests, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &engine.ExecutionPayloadBody{
|
return &engine.ExecutionPayloadBody{
|
||||||
TransactionData: txs,
|
TransactionData: txs,
|
||||||
Withdrawals: withdrawals,
|
Withdrawals: withdrawals,
|
||||||
Deposits: depositRequests,
|
Deposits: depositRequests,
|
||||||
|
WithdrawalRequests: withdrawalRequests,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,10 +113,18 @@ func (miner *Miner) generateWork(params *generateParams) *newPayloadResult {
|
||||||
}
|
}
|
||||||
// Read requests if Prague is enabled.
|
// Read requests if Prague is enabled.
|
||||||
if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) {
|
if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) {
|
||||||
requests, err := core.ParseDepositLogs(allLogs, miner.chainConfig)
|
// Parse Deposits
|
||||||
|
requests := make([]*types.Request, 0)
|
||||||
|
reqs, err := core.ParseDepositLogs(allLogs, miner.chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
|
requests = append(requests, reqs...)
|
||||||
|
// Process WithdrawalRequests
|
||||||
|
context := core.NewEVMBlockContext(work.header, miner.chain, nil)
|
||||||
|
vmenv := vm.NewEVM(context, vm.TxContext{}, work.state, miner.chainConfig, vm.Config{})
|
||||||
|
wxs := core.ProcessDequeueWithdrawalRequests(vmenv, work.state)
|
||||||
|
requests = append(requests, wxs...)
|
||||||
body.Requests = requests
|
body.Requests = requests
|
||||||
}
|
}
|
||||||
block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
|
block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts)
|
||||||
|
|
|
||||||
|
|
@ -189,10 +189,10 @@ var (
|
||||||
|
|
||||||
// BeaconRootsAddress is the address where historical beacon roots are stored as per EIP-4788
|
// BeaconRootsAddress is the address where historical beacon roots are stored as per EIP-4788
|
||||||
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
|
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
|
||||||
|
|
||||||
// BeaconRootsCode is the code where historical beacon roots are stored as per EIP-4788
|
// BeaconRootsCode is the code where historical beacon roots are stored as per EIP-4788
|
||||||
BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
||||||
|
// WithdrawalRequests is the address where the EIP-7002 withdrawal requests queue is maintained.
|
||||||
|
WithdrawalRequestsAddress = common.HexToAddress("0x00A3ca265EBcb825B45F985A16CEFB49958cE017")
|
||||||
// SystemAddress is where the system-transaction is sent from as per EIP-4788
|
// SystemAddress is where the system-transaction is sent from as per EIP-4788
|
||||||
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
|
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
|
||||||
// HistoryStorageAddress is where the historical block hashes are stored.
|
// HistoryStorageAddress is where the historical block hashes are stored.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue