diff --git a/beacon/engine/gen_ed.go b/beacon/engine/gen_ed.go index 64e4453d4bd7..2c8c04eb1eae 100644 --- a/beacon/engine/gen_ed.go +++ b/beacon/engine/gen_ed.go @@ -17,25 +17,26 @@ var _ = (*executableDataMarshaling)(nil) // MarshalJSON marshals as JSON. func (e ExecutableData) MarshalJSON() ([]byte, error) { type ExecutableData struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - Deposits types.Deposits `json:"depositRequests"` - WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + Deposits types.Deposits `json:"depositRequests"` + WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests"` + ConsolidationRequests types.ConsolidationRequests `json:"consolidationRequests"` } var enc ExecutableData enc.ParentHash = e.ParentHash @@ -62,31 +63,33 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas) enc.Deposits = e.Deposits enc.WithdrawalRequests = e.WithdrawalRequests + enc.ConsolidationRequests = e.ConsolidationRequests return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (e *ExecutableData) UnmarshalJSON(input []byte) error { type ExecutableData struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Random *common.Hash `json:"prevRandao" gencodec:"required"` - Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` - BlockHash *common.Hash `json:"blockHash" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - Deposits *types.Deposits `json:"depositRequests"` - WithdrawalRequests *types.WithdrawalRequests `json:"withdrawalRequests"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random *common.Hash `json:"prevRandao" gencodec:"required"` + Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + BlockHash *common.Hash `json:"blockHash" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + Deposits *types.Deposits `json:"depositRequests"` + WithdrawalRequests *types.WithdrawalRequests `json:"withdrawalRequests"` + ConsolidationRequests *types.ConsolidationRequests `json:"consolidationRequests"` } var dec ExecutableData if err := json.Unmarshal(input, &dec); err != nil { @@ -166,5 +169,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.WithdrawalRequests != nil { e.WithdrawalRequests = *dec.WithdrawalRequests } + if dec.ConsolidationRequests != nil { + e.ConsolidationRequests = *dec.ConsolidationRequests + } return nil } diff --git a/beacon/engine/types.go b/beacon/engine/types.go index c27d8dbb4669..7004caf623d6 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -59,25 +59,26 @@ type payloadAttributesMarshaling struct { // ExecutableData is the data necessary to execute an EL payload. type ExecutableData struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom []byte `json:"logsBloom" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - Number uint64 `json:"blockNumber" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` - Timestamp uint64 `json:"timestamp" gencodec:"required"` - ExtraData []byte `json:"extraData" gencodec:"required"` - BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - Transactions [][]byte `json:"transactions" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals"` - BlobGasUsed *uint64 `json:"blobGasUsed"` - ExcessBlobGas *uint64 `json:"excessBlobGas"` - Deposits types.Deposits `json:"depositRequests"` - WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom []byte `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + Number uint64 `json:"blockNumber" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Timestamp uint64 `json:"timestamp" gencodec:"required"` + ExtraData []byte `json:"extraData" gencodec:"required"` + BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + Transactions [][]byte `json:"transactions" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + BlobGasUsed *uint64 `json:"blobGasUsed"` + ExcessBlobGas *uint64 `json:"excessBlobGas"` + Deposits types.Deposits `json:"depositRequests"` + WithdrawalRequests types.WithdrawalRequests `json:"withdrawalRequests"` + ConsolidationRequests types.ConsolidationRequests `json:"consolidationRequests"` } // JSON type overrides for executableData. @@ -247,6 +248,9 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, if params.WithdrawalRequests != nil { requests = append(requests, params.WithdrawalRequests.Requests()...) } + if params.ConsolidationRequests != nil { + requests = append(requests, params.ConsolidationRequests.Requests()...) + } if requests != nil { h := types.DeriveSha(requests, trie.NewStackTrie(nil)) requestsHash = &h @@ -293,25 +297,26 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, // fields from the given block. It assumes the given block is post-merge block. func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *ExecutionPayloadEnvelope { data := &ExecutableData{ - BlockHash: block.Hash(), - ParentHash: block.ParentHash(), - FeeRecipient: block.Coinbase(), - StateRoot: block.Root(), - Number: block.NumberU64(), - GasLimit: block.GasLimit(), - GasUsed: block.GasUsed(), - BaseFeePerGas: block.BaseFee(), - Timestamp: block.Time(), - ReceiptsRoot: block.ReceiptHash(), - LogsBloom: block.Bloom().Bytes(), - Transactions: encodeTransactions(block.Transactions()), - Random: block.MixDigest(), - ExtraData: block.Extra(), - Withdrawals: block.Withdrawals(), - BlobGasUsed: block.BlobGasUsed(), - ExcessBlobGas: block.ExcessBlobGas(), - Deposits: block.Deposits(), - WithdrawalRequests: block.WithdrawalRequests(), + BlockHash: block.Hash(), + ParentHash: block.ParentHash(), + FeeRecipient: block.Coinbase(), + StateRoot: block.Root(), + Number: block.NumberU64(), + GasLimit: block.GasLimit(), + GasUsed: block.GasUsed(), + BaseFeePerGas: block.BaseFee(), + Timestamp: block.Time(), + ReceiptsRoot: block.ReceiptHash(), + LogsBloom: block.Bloom().Bytes(), + Transactions: encodeTransactions(block.Transactions()), + Random: block.MixDigest(), + ExtraData: block.Extra(), + Withdrawals: block.Withdrawals(), + BlobGasUsed: block.BlobGasUsed(), + ExcessBlobGas: block.ExcessBlobGas(), + Deposits: block.Deposits(), + WithdrawalRequests: block.WithdrawalRequests(), + ConsolidationRequests: block.ConsolidationRequests(), } bundle := BlobsBundleV1{ Commitments: make([]hexutil.Bytes, 0), diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 255d1d5d306d..8ebebdc87e82 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -53,22 +53,23 @@ type Prestate struct { // ExecutionResult contains the execution status after running a state test, any // error that might have occurred and a dump of the final state if requested. type ExecutionResult struct { - StateRoot common.Hash `json:"stateRoot"` - TxRoot common.Hash `json:"txRoot"` - ReceiptRoot common.Hash `json:"receiptsRoot"` - LogsHash common.Hash `json:"logsHash"` - Bloom types.Bloom `json:"logsBloom" gencodec:"required"` - Receipts types.Receipts `json:"receipts"` - Rejected []*rejectedTx `json:"rejected,omitempty"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` - GasUsed math.HexOrDecimal64 `json:"gasUsed"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` - WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` - CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` - CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` - RequestsHash *common.Hash `json:"requestsRoot,omitempty"` - DepositRequests *types.Deposits `json:"depositRequests,omitempty"` - WithdrawalRequests *types.WithdrawalRequests `json:"withdrawalRequests,omitempty"` + StateRoot common.Hash `json:"stateRoot"` + TxRoot common.Hash `json:"txRoot"` + ReceiptRoot common.Hash `json:"receiptsRoot"` + LogsHash common.Hash `json:"logsHash"` + Bloom types.Bloom `json:"logsBloom" gencodec:"required"` + Receipts types.Receipts `json:"receipts"` + Rejected []*rejectedTx `json:"rejected,omitempty"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` + WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` + CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` + CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` + RequestsHash *common.Hash `json:"requestsRoot,omitempty"` + DepositRequests *types.Deposits `json:"depositRequests,omitempty"` + WithdrawalRequests *types.WithdrawalRequests `json:"withdrawalRequests,omitempty"` + ConsolidationRequests *types.ConsolidationRequests `json:"consolidationRequests,omitempty"` } type ommer struct { @@ -350,9 +351,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } // Retrieve deposit and withdrawal requests var ( - depositRequests *types.Deposits - withdrawalRequests *types.WithdrawalRequests - requestsHash *common.Hash + depositRequests *types.Deposits + withdrawalRequests *types.WithdrawalRequests + consolidationRequests *types.ConsolidationRequests + requestsHash *common.Hash ) if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) { // Parse deposit requests from the logs @@ -368,6 +370,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, vmenv := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) wxs := core.ProcessDequeueWithdrawalRequests(vmenv, statedb) requests = append(requests, wxs...) + // Process the consolidation requests contract execution + cxs := core.ProcessDequeueConsolidationRequests(vmenv, statedb) + requests = append(requests, cxs...) // Calculate the requests root h := types.DeriveSha(requests, trie.NewStackTrie(nil)) requestsHash = &h @@ -377,6 +382,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // Get the withdrawals from the requests withdrawals := requests.Withdrawals() withdrawalRequests = &withdrawals + // Get the consolidations from the requests + consolidations := requests.Consolidations() + consolidationRequests = &consolidations } // Commit block root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber)) @@ -384,19 +392,20 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err)) } execRs := &ExecutionResult{ - StateRoot: root, - TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), - ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)), - Bloom: types.CreateBloom(receipts), - LogsHash: rlpHash(statedb.Logs()), - Receipts: receipts, - Rejected: rejectedTxs, - Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), - GasUsed: (math.HexOrDecimal64)(gasUsed), - BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), - RequestsHash: requestsHash, - DepositRequests: depositRequests, - WithdrawalRequests: withdrawalRequests, + StateRoot: root, + TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), + ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)), + Bloom: types.CreateBloom(receipts), + LogsHash: rlpHash(statedb.Logs()), + Receipts: receipts, + Rejected: rejectedTxs, + Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), + GasUsed: (math.HexOrDecimal64)(gasUsed), + BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), + RequestsHash: requestsHash, + DepositRequests: depositRequests, + WithdrawalRequests: withdrawalRequests, + ConsolidationRequests: consolidationRequests, } if pre.Env.Withdrawals != nil { h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) diff --git a/core/chain_makers.go b/core/chain_makers.go index b8b1a1ea738d..4b026cae59a6 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -362,6 +362,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse ) wxs := ProcessDequeueWithdrawalRequests(vmenv, statedb) requests = append(requests, wxs...) + cxs := ProcessDequeueConsolidationRequests(vmenv, statedb) + requests = append(requests, cxs...) } body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals, Requests: requests} diff --git a/core/requests_test.go b/core/requests_test.go index 63f014430480..2539ce88b133 100644 --- a/core/requests_test.go +++ b/core/requests_test.go @@ -18,9 +18,8 @@ import ( "github.com/ethereum/go-ethereum/params" ) -// 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) { +// TestRequests verifies that Prague requests are processed correctly. +func TestRequests(t *testing.T) { var ( engine = beacon.NewFaker() @@ -32,8 +31,9 @@ func TestEIP7002(t *testing.T) { gspec = &Genesis{ Config: &config, Alloc: types.GenesisAlloc{ - addr: {Balance: funds}, - params.WithdrawalRequestsAddress: {Code: common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b366038141561012e5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012e57600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f548061049d141561010757505f5b60015460028282011161011c5750505f610122565b01600290035b5f555f600155604c025ff35b5f5ffd")}, + addr: {Balance: funds}, + params.WithdrawalRequestsAddress: {Code: common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b366038141561012e5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012e57600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f548061049d141561010757505f5b60015460028282011161011c5750505f610122565b01600290035b5f555f600155604c025ff35b5f5ffd")}, + params.ConsolidationRequestsAddress: {Code: common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe146098573615156028575f545f5260205ff35b36606014156101445760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061014457600154600101600155600354806004026004013381556001015f35815560010160203581556001016040359055600101600355005b6003546002548082038060011160ac575060015b5f5b81811460f15780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160ae565b9101809214610103579060025561010e565b90505f6002555f6003555b5f548061049d141561011d57505f5b6001546001828201116101325750505f610138565b01600190035b5f555f6001556074025ff35b5f5ffd")}, }, } ) @@ -59,28 +59,66 @@ func TestEIP7002(t *testing.T) { Amount: 1337, }, } + cxs := types.ConsolidationRequests{ + { + Source: addr, + SourcePublicKey: [48]byte{13, 37}, + TargetPublicKey: [48]byte{11, 11}, + }, + { + Source: addr, + SourcePublicKey: [48]byte{42, 42}, + TargetPublicKey: [48]byte{11, 11}, + }, + } - _, 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, + _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 3, func(i int, b *BlockGen) { + switch i { + case 0: + // Block 1: submit withdrawal requests + for _, 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: b.TxNonce(addr), + 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) + } + case 1: + // Block 2: submit consolidation requests + for _, cx := range cxs { + data := make([]byte, 96) + copy(data, cx.SourcePublicKey[:]) + copy(data[48:], cx.TargetPublicKey[:]) + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: b.TxNonce(addr), + To: ¶ms.ConsolidationRequestsAddress, + 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) } - 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) @@ -89,15 +127,15 @@ func TestEIP7002(t *testing.T) { if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } + + // Verify the withdrawal requests match. block := chain.GetBlockByNumber(1) if block == nil { t.Fatalf("failed to retrieve block 1") } - - // Verify the withdrawal requests match. got := block.WithdrawalRequests() if len(got) != 2 { - t.Fatalf("wrong number of withdrawal requests: wanted 2, got %d", len(wxs)) + t.Fatalf("wrong number of withdrawal requests: wanted 2, got %d", len(got)) } for i, want := range wxs { if want.Source != got[i].Source { @@ -110,4 +148,25 @@ func TestEIP7002(t *testing.T) { t.Fatalf("wrong amount: want %d, got %d", want.Amount, got[i].Amount) } } + + // Verify the consolidation requests match. + for i, want := range cxs { + block := chain.GetBlockByNumber(uint64(i + 2)) + if block == nil { + t.Fatalf("failed to retrieve block 2") + } + if got := block.ConsolidationRequests(); len(got) != 1 { + t.Fatalf("wrong number of consolidation requests: wanted 1, got %d", len(got)) + } + got := block.ConsolidationRequests()[0] + if want.Source != got.Source { + t.Fatalf("wrong source address: want %s, got %s", want.Source, got.Source) + } + if want.SourcePublicKey != got.SourcePublicKey { + t.Fatalf("wrong source public key: want %s, got %s", common.Bytes2Hex(want.SourcePublicKey[:]), common.Bytes2Hex(got.SourcePublicKey[:])) + } + if want.TargetPublicKey != got.TargetPublicKey { + t.Fatalf("wrong target public key: want %s, got %s", common.Bytes2Hex(want.TargetPublicKey[:]), common.Bytes2Hex(got.TargetPublicKey[:])) + } + } } diff --git a/core/state_processor.go b/core/state_processor.go index b7f1dbb3cc8e..8e352267aca0 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -109,6 +109,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } wxs := ProcessDequeueWithdrawalRequests(vmenv, statedb) requests = append(requests, wxs...) + cxs := ProcessDequeueConsolidationRequests(vmenv, statedb) + requests = append(requests, cxs...) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) @@ -278,3 +280,44 @@ func ProcessDequeueWithdrawalRequests(vmenv *vm.EVM, statedb *state.StateDB) typ } return reqs } + +// ProcessDequeueConsolidationRequests applies the EIP-7251 system call to the consolidation requests contract. +func ProcessDequeueConsolidationRequests(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.ConsolidationRequestsAddress, + } + vmenv.Reset(NewEVMTxContext(msg), statedb) + statedb.AddAddressToAccessList(params.ConsolidationRequestsAddress) + 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)/116; i++ { + start := i * 116 + var ( + sourcePubkey [48]byte + targetPubkey [48]byte + ) + copy(sourcePubkey[:], ret[start+20:start+20+48]) + copy(targetPubkey[:], ret[start+20+48:start+20+48+48]) + cx := &types.ConsolidationRequest{ + Source: common.BytesToAddress(ret[start : start+20]), + SourcePublicKey: sourcePubkey, + TargetPublicKey: targetPubkey, + } + reqs = append(reqs, types.NewRequest(cx)) + } + return reqs +} diff --git a/core/types/block.go b/core/types/block.go index 7d65a43d3358..7c87ea386dbf 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -394,6 +394,21 @@ func (b *Block) WithdrawalRequests() WithdrawalRequests { return wxs } +func (b *Block) ConsolidationRequests() ConsolidationRequests { + var cxs ConsolidationRequests + if b.requests != nil { + // If requests is non-nil, it means deposits are available in block and we + // should return an empty slice instead of nil if there are no deposits. + cxs = make(ConsolidationRequests, 0) + } + for _, r := range b.requests { + if c, ok := r.inner.(*ConsolidationRequest); ok { + cxs = append(cxs, c) + } + } + return cxs +} + func (b *Block) Transaction(hash common.Hash) *Transaction { for _, transaction := range b.transactions { if transaction.Hash() == hash { diff --git a/core/types/consolidation_request.go b/core/types/consolidation_request.go new file mode 100644 index 000000000000..4c3e8e49b224 --- /dev/null +++ b/core/types/consolidation_request.go @@ -0,0 +1,79 @@ +// 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 . +package types + +import ( + "bytes" + + "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 ConsolidationRequest -field-override consolidationRequestMarshaling -out gen_consolidation_request_json.go + +// ConsolidationRequest represents an EIP-7251 consolidation request from source for +// the validator associated with the source public key to a target public key. +type ConsolidationRequest struct { + Source common.Address `json:"sourceAddress"` + SourcePublicKey [48]byte `json:"sourcePubkey"` + TargetPublicKey [48]byte `json:"targetPubkey"` +} + +// field type overrides for gencodec +type consolidationRequestMarshaling struct { + SourcePublicKey hexutil.Bytes + TargetPublicKey hexutil.Bytes +} + +func (c *ConsolidationRequest) Bytes() []byte { + out := make([]byte, 116) + copy(out, c.Source.Bytes()) + copy(out[20:], c.SourcePublicKey[:]) + copy(out[68:], c.TargetPublicKey[:]) + return out +} + +// ConsolidationRequests implements DerivableList for consolidation requests. +type ConsolidationRequests []*ConsolidationRequest + +// Len returns the length of s. +func (s ConsolidationRequests) Len() int { return len(s) } + +// EncodeIndex encodes the i'th consolidation request to c. +func (s ConsolidationRequests) EncodeIndex(i int, c *bytes.Buffer) { + rlp.Encode(c, s[i]) +} + +// Requests creates a deep copy of each deposit and returns a slice of the +// withdrwawal requests as Request objects. +func (s ConsolidationRequests) Requests() (reqs Requests) { + for _, d := range s { + reqs = append(reqs, NewRequest(d)) + } + return +} + +func (c *ConsolidationRequest) requestType() byte { return ConsolidationRequestType } +func (c *ConsolidationRequest) encode(b *bytes.Buffer) error { return rlp.Encode(b, c) } +func (c *ConsolidationRequest) decode(input []byte) error { return rlp.DecodeBytes(input, c) } +func (c *ConsolidationRequest) copy() RequestData { + return &ConsolidationRequest{ + Source: c.Source, + SourcePublicKey: c.SourcePublicKey, + TargetPublicKey: c.TargetPublicKey, + } +} diff --git a/core/types/gen_consolidation_request_json.go b/core/types/gen_consolidation_request_json.go new file mode 100644 index 000000000000..699abb4a7d9c --- /dev/null +++ b/core/types/gen_consolidation_request_json.go @@ -0,0 +1,56 @@ +// 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 _ = (*consolidationRequestMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (c ConsolidationRequest) MarshalJSON() ([]byte, error) { + type ConsolidationRequest struct { + Source common.Address `json:"sourceAddress"` + SourcePublicKey hexutil.Bytes `json:"sourcePubkey"` + TargetPublicKey hexutil.Bytes `json:"targetPubkey"` + } + var enc ConsolidationRequest + enc.Source = c.Source + enc.SourcePublicKey = c.SourcePublicKey[:] + enc.TargetPublicKey = c.TargetPublicKey[:] + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (c *ConsolidationRequest) UnmarshalJSON(input []byte) error { + type ConsolidationRequest struct { + Source *common.Address `json:"sourceAddress"` + SourcePublicKey *hexutil.Bytes `json:"sourcePubkey"` + TargetPublicKey *hexutil.Bytes `json:"targetPubkey"` + } + var dec ConsolidationRequest + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Source != nil { + c.Source = *dec.Source + } + if dec.SourcePublicKey != nil { + if len(*dec.SourcePublicKey) != len(c.SourcePublicKey) { + return errors.New("field 'sourcePubkey' has wrong length, need 48 items") + } + copy(c.SourcePublicKey[:], *dec.SourcePublicKey) + } + if dec.TargetPublicKey != nil { + if len(*dec.TargetPublicKey) != len(c.TargetPublicKey) { + return errors.New("field 'targetPubkey' has wrong length, need 48 items") + } + copy(c.TargetPublicKey[:], *dec.TargetPublicKey) + } + return nil +} diff --git a/core/types/request.go b/core/types/request.go index 0033a0bb079b..eb8873a019f2 100644 --- a/core/types/request.go +++ b/core/types/request.go @@ -32,8 +32,9 @@ var ( // Request types. const ( - DepositRequestType = 0x00 - WithdrawalRequestType = 0x01 + DepositRequestType = 0x00 + WithdrawalRequestType = 0x01 + ConsolidationRequestType = 0x02 ) // Request is an EIP-7685 request object. It represents execution layer @@ -87,6 +88,17 @@ func (s Requests) Withdrawals() WithdrawalRequests { return wr } +// Retrieve consolidation requests from a requests list. +func (s Requests) Consolidations() ConsolidationRequests { + cr := make(ConsolidationRequests, 0, len(s)) + for _, req := range s { + if req.Type() == ConsolidationRequestType { + cr = append(cr, req.inner.(*ConsolidationRequest)) + } + } + return cr +} + type RequestData interface { requestType() byte encode(*bytes.Buffer) error @@ -168,6 +180,8 @@ func (r *Request) decode(b []byte) (RequestData, error) { inner = new(Deposit) case WithdrawalRequestType: inner = new(WithdrawalRequest) + case ConsolidationRequestType: + inner = new(ConsolidationRequest) default: return nil, ErrRequestTypeNotSupported } diff --git a/miner/worker.go b/miner/worker.go index 2e3610120169..532d815e6bbe 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -125,6 +125,9 @@ func (miner *Miner) generateWork(params *generateParams) *newPayloadResult { vmenv := vm.NewEVM(context, vm.TxContext{}, work.state, miner.chainConfig, vm.Config{}) wxs := core.ProcessDequeueWithdrawalRequests(vmenv, work.state) requests = append(requests, wxs...) + // Process ConsolidationRequests + cxs := core.ProcessDequeueConsolidationRequests(vmenv, work.state) + requests = append(requests, cxs...) body.Requests = requests } block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts) diff --git a/params/protocol_params.go b/params/protocol_params.go index 2ba7ef758f3a..9e2aad27891a 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -191,6 +191,8 @@ var ( BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500") // WithdrawalRequests is the address where the EIP-7002 withdrawal requests queue is maintained. WithdrawalRequestsAddress = common.HexToAddress("0x00A3ca265EBcb825B45F985A16CEFB49958cE017") + // ConsolidationRequests is the address where the EIP-7251 consolidation requests queue is maintained. + ConsolidationRequestsAddress = common.HexToAddress("0x00b42dbF2194e931E80326D950320f7d9Dbeac02") // SystemAddress is where the system-transaction is sent from as per EIP-4788 SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") )