Skip to content

Commit

Permalink
all: implement flat deposit requests encoding (#30425)
Browse files Browse the repository at this point in the history
This implements recent changes to EIP-7685, EIP-6110, and
execution-apis.

---------

Co-authored-by: lightclient <[email protected]>
Co-authored-by: Shude Li <[email protected]>
  • Loading branch information
3 people authored Oct 9, 2024
1 parent f8ac95e commit 2936b41
Show file tree
Hide file tree
Showing 27 changed files with 222 additions and 627 deletions.
6 changes: 0 additions & 6 deletions beacon/engine/gen_ed.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions beacon/engine/gen_epe.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 11 additions & 33 deletions beacon/engine/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ type ExecutableData struct {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
BlobGasUsed *uint64 `json:"blobGasUsed"`
ExcessBlobGas *uint64 `json:"excessBlobGas"`
Deposits types.Deposits `json:"depositRequests"`
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
}

Expand Down Expand Up @@ -108,6 +107,7 @@ type ExecutionPayloadEnvelope struct {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
Requests [][]byte `json:"executionRequests"`
Override bool `json:"shouldOverrideBuilder"`
Witness *hexutil.Bytes `json:"witness"`
}
Expand All @@ -121,6 +121,7 @@ type BlobsBundleV1 struct {
// JSON type overrides for ExecutionPayloadEnvelope.
type executionPayloadEnvelopeMarshaling struct {
BlockValue *hexutil.Big
Requests []hexutil.Bytes
}

type PayloadStatusV1 struct {
Expand Down Expand Up @@ -207,8 +208,8 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
// and that the blockhash of the constructed block matches the parameters. Nil
// Withdrawals value will propagate through the returned block. Empty
// Withdrawals value must be passed via non-nil, length 0 value in data.
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot)
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) {
block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests)
if err != nil {
return nil, err
}
Expand All @@ -221,7 +222,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
// ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used
// for stateless execution, so it skips checking if the executable data hashes to
// the requested hash (stateless has to *compute* the root hash, it's not given).
func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) {
txs, err := decodeTransactions(data.Transactions)
if err != nil {
return nil, err
Expand Down Expand Up @@ -256,19 +257,13 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
withdrawalsRoot = &h
}
// Compute requestsHash if any requests are non-nil.
var (
requestsHash *common.Hash
requests types.Requests
)
if data.Deposits != nil {
requests = make(types.Requests, 0)
for _, d := range data.Deposits {
requests = append(requests, types.NewRequest(d))
}
h := types.DeriveSha(requests, trie.NewStackTrie(nil))

var requestsHash *common.Hash
if requests != nil {
h := types.CalcRequestsHash(requests)
requestsHash = &h
}

header := &types.Header{
ParentHash: data.ParentHash,
UncleHash: types.EmptyUncleHash,
Expand All @@ -292,7 +287,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
RequestsHash: requestsHash,
}
return types.NewBlockWithHeader(header).
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Requests: requests}).
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}).
WithWitness(data.ExecutionWitness),
nil
}
Expand Down Expand Up @@ -332,30 +327,13 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:]))
}
}
setRequests(block.Requests(), data)
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle, Override: false}
}

// setRequests differentiates the different request types and
// assigns them to the associated fields in ExecutableData.
func setRequests(requests types.Requests, data *ExecutableData) {
if 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.
data.Deposits = make(types.Deposits, 0)
}
for _, r := range requests {
if d, ok := r.Inner().(*types.Deposit); ok {
data.Deposits = append(data.Deposits, d)
}
}
}

// ExecutionPayloadBody is used in the response to GetPayloadBodiesByHash and GetPayloadBodiesByRange
type ExecutionPayloadBody struct {
TransactionData []hexutil.Bytes `json:"transactions"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
Deposits types.Deposits `json:"depositRequests"`
}

// Client identifiers to support ClientVersionV1.
Expand Down
16 changes: 5 additions & 11 deletions cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ type ExecutionResult struct {
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"`
RequestsHash *common.Hash `json:"requestsRoot,omitempty"`
DepositRequests *types.Deposits `json:"depositRequests,omitempty"`
Requests [][]byte `json:"requests,omitempty"`
}

type ommer struct {
Expand Down Expand Up @@ -385,21 +385,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
for _, receipt := range receipts {
allLogs = append(allLogs, receipt.Logs...)
}
requests, err := core.ParseDepositLogs(allLogs, chainConfig)
depositRequests, err := core.ParseDepositLogs(allLogs, chainConfig)
if err != nil {
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
}
requests := [][]byte{depositRequests}
// Calculate the requests root
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
h := types.CalcRequestsHash(requests)
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
execRs.Requests = requests
}
// Re-create statedb instance with new root upon the updated database
// for accessing latest states.
Expand Down
8 changes: 5 additions & 3 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,12 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
}
// Validate the parsed requests match the expected header value.
if header.RequestsHash != nil {
depositSha := types.DeriveSha(res.Requests, trie.NewStackTrie(nil))
if depositSha != *header.RequestsHash {
return fmt.Errorf("invalid deposit root hash (remote: %x local: %x)", *header.RequestsHash, depositSha)
reqhash := types.CalcRequestsHash(res.Requests)
if reqhash != *header.RequestsHash {
return fmt.Errorf("invalid requests hash (remote: %x local: %x)", *header.RequestsHash, reqhash)
}
} else if res.Requests != nil {
return fmt.Errorf("block has requests before prague fork")
}
// Validate the state root against the received state root and throw
// an error if they don't match.
Expand Down
87 changes: 0 additions & 87 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4227,90 +4227,3 @@ func TestEIP3651(t *testing.T) {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
}
}

func TestEIP6110(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},
config.DepositContractAddress: {
// Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7
Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"),
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
)

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)

_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
for i := 0; i < 5; i++ {
txdata := &types.DynamicFeeTx{
ChainID: gspec.Config.ChainID,
Nonce: uint64(i),
To: &config.DepositContractAddress,
Gas: 500000,
GasFeeCap: newGwei(5),
GasTipCap: big.NewInt(2),
AccessList: nil,
Data: []byte{},
}
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{DisableStack: true}, os.Stderr).Hooks()}, 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 len(block.Requests()) != 5 {
t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5)
}

// Verify each index is correct.
for want, req := range block.Requests() {
d, ok := req.Inner().(*types.Deposit)
if !ok {
t.Fatalf("expected deposit object")
}
if got := int(d.PublicKey[0]); got != want {
t.Fatalf("invalid pubkey: have %d, want %d", got, want)
}
if got := int(d.WithdrawalCredentials[0]); got != want {
t.Fatalf("invalid withdrawal credentials: have %d, want %d", got, want)
}
if d.Amount != uint64(want) {
t.Fatalf("invalid amounbt: have %d, want %d", d.Amount, want)
}
if got := int(d.Signature[0]); got != want {
t.Fatalf("invalid signature: have %d, want %d", got, want)
}
if d.Index != uint64(want) {
t.Fatalf("invalid index: have %d, want %d", d.Index, want)
}
}
}
20 changes: 13 additions & 7 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,18 +346,24 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
gen(i, b)
}

var requests types.Requests
var requests [][]byte
if config.IsPrague(b.header.Number, b.header.Time) {
var blockLogs []*types.Log
for _, r := range b.receipts {
d, err := ParseDepositLogs(r.Logs, config)
if err != nil {
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
}
requests = append(requests, d...)
blockLogs = append(blockLogs, r.Logs...)
}
depositRequests, err := ParseDepositLogs(blockLogs, config)
if err != nil {
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
}
requests = append(requests, depositRequests)
}
if requests != nil {
reqHash := types.CalcRequestsHash(requests)
b.header.RequestsHash = &reqHash
}

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}
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
if err != nil {
panic(err)
Expand Down
8 changes: 4 additions & 4 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,6 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
}
var (
withdrawals []*types.Withdrawal
requests types.Requests
)
if conf := g.Config; conf != nil {
num := big.NewInt(int64(g.Number))
Expand All @@ -473,11 +472,12 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
}
}
if conf.IsPrague(num, g.Timestamp) {
head.RequestsHash = &types.EmptyRequestsHash
requests = make(types.Requests, 0)
emptyRequests := [][]byte{{0}}
rhash := types.CalcRequestsHash(emptyRequests)
head.RequestsHash = &rhash
}
}
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals, Requests: requests}, nil, trie.NewStackTrie(nil))
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
}

// Commit writes the block and state of a genesis specification to the database.
Expand Down
Loading

0 comments on commit 2936b41

Please sign in to comment.