Skip to content

Implement Deposit Requests #2748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions kevm-pyk/src/kevm_pyk/kproj/evm-semantics/driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ Here we check the other post-conditions associated with an EVM test.
SetItem("mixHash") SetItem("nonce") SetItem("number") SetItem("parentHash")
SetItem("receiptTrie") SetItem("stateRoot") SetItem("timestamp")
SetItem("transactionsTrie") SetItem("uncleHash") SetItem("baseFeePerGas") SetItem("withdrawalsRoot")
SetItem("blobGasUsed") SetItem("excessBlobGas") SetItem("parentBeaconBlockRoot")
SetItem("blobGasUsed") SetItem("excessBlobGas") SetItem("parentBeaconBlockRoot") SetItem("requestsHash")
)

rule <k> check "blockHeader" : { "bloom" : VALUE } => .K ... </k> <logsBloom> VALUE </logsBloom>
Expand All @@ -568,6 +568,7 @@ Here we check the other post-conditions associated with an EVM test.
rule <k> check "blockHeader" : { "blobGasUsed" : VALUE } => .K ... </k> <blobGasUsed> VALUE </blobGasUsed>
rule <k> check "blockHeader" : { "excessBlobGas" : VALUE } => .K ... </k> <excessBlobGas> VALUE </excessBlobGas>
rule <k> check "blockHeader" : { "parentBeaconBlockRoot": VALUE } => .K ... </k> <beaconRoot> VALUE </beaconRoot>
rule <k> check "blockHeader" : { "requestsHash" : VALUE } => .K ... </k> <requestsRoot> VALUE </requestsRoot>


rule <k> check "blockHeader" : { "hash": HASH:Bytes } => .K ...</k>
Expand All @@ -591,10 +592,12 @@ Here we check the other post-conditions associated with an EVM test.
<blobGasUsed> UB </blobGasUsed>
<excessBlobGas> EB </excessBlobGas>
<beaconRoot> BR </beaconRoot>
requires #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN) ==Int #asWord(HASH)
orBool #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN, HF) ==Int #asWord(HASH)
orBool #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN, HF, WR) ==Int #asWord(HASH)
orBool #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN, HF, WR, UB, EB, BR) ==Int #asWord(HASH)
<requestsRoot> RR </requestsRoot>
requires #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN) ==Int #asWord(HASH)
orBool #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN, HF) ==Int #asWord(HASH)
orBool #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN, HF, WR) ==Int #asWord(HASH)
orBool #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN, HF, WR, UB, EB, BR) ==Int #asWord(HASH)
orBool #blockHeaderHash(HP, HO, HC, HR, HT, HE, HB, HD, HI, HL, HG, HS, HX, HM, HN, HF, WR, UB, EB, BR, RR) ==Int #asWord(HASH)

rule check TESTID : { "genesisBlockHeader" : BLOCKHEADER } => check "genesisBlockHeader" : BLOCKHEADER ~> failure TESTID
// ------------------------------------------------------------------------------------------------------------------------
Expand Down
63 changes: 61 additions & 2 deletions kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ This file only defines the local execution operations, the file `driver.md` will
requires "data.md"
requires "network.md"
requires "gas.md"
requires "requests.md"

module EVM
imports STRING
imports EVM-DATA
imports NETWORK
imports GAS
imports EVM-REQUESTS
```

Configuration
Expand Down Expand Up @@ -118,6 +120,7 @@ In the comments next to each cell, we've marked which component of the YellowPap
<blobGasUsed> 0 </blobGasUsed>
<excessBlobGas> 0 </excessBlobGas>
<beaconRoot> 0 </beaconRoot>
<requestsRoot> 0 </requestsRoot>

<ommerBlockHeaders> [ .JSONs ] </ommerBlockHeaders>
</block>
Expand Down Expand Up @@ -192,6 +195,11 @@ In the comments next to each cell, we've marked which component of the YellowPap
</withdrawal>
</withdrawals>

<requests>
<depositRequests> .List </depositRequests>
//other request types come here
</requests>

</network>

</ethereum>
Expand Down Expand Up @@ -714,6 +722,53 @@ After executing a transaction, it's necessary to have the effect of the substate
rule <k> #deleteAccounts(.List) => .K ... </k>
```

### Fetching requests from event logs

While processing a block, multiple requests objects with different `request_types` will be produced by the system, and accumulated in the block requests list.

```k
syntax KItem ::= "#filterLogs" Int [symbol(#filterLogs)]
// --------------------------------------------------------
rule <k> #filterLogs _ => .K ... </k> <schedule> SCHED </schedule> requires notBool Ghasrequests << SCHED >>

rule <k> #filterLogs IDX => .K ... </k>
<schedule> SCHED </schedule>
<log> LOGS </log>
<depositRequests> DRQSTS </depositRequests>
<requestsRoot> _ => #computeRequestsHash(DRQSTS) </requestsRoot>
requires Ghasrequests << SCHED >> andBool IDX >=Int size(LOGS)

rule <k> #filterLogs IDX
=> #parseDepositRequest {LOGS[IDX]}:>SubstateLogEntry
// parse other request types
~> #filterLogs IDX +Int 1
...
</k>
<log> LOGS </log>
<schedule> SCHED </schedule>
requires IDX <Int size(LOGS) andBool Ghasrequests << SCHED >>
```

Rules for parsing Deposit Requests according to EIP-6110.

```k
syntax KItem ::= "#parseDepositRequest" SubstateLogEntry [symbol(#parseDepositRequest)]
// ---------------------------------------------------------------------------------------
rule <k> #parseDepositRequest { ADDR | TOPICS | DATA } => .K ... </k>
<depositRequests> RS => RS ListItem(Int2Bytes(1, DEPOSIT_REQUEST_TYPE, BE) +Bytes #extractDepositData(DATA)) </depositRequests>
requires ADDR ==K DEPOSIT_CONTRACT_ADDRESS
andBool size(TOPICS) >Int 0
andBool {TOPICS[0]}:>Int ==Int DEPOSIT_EVENT_SIGNATURE_HASH
andBool #isValidDepositEventData(DATA)

rule <k> #parseDepositRequest { ADDR | TOPICS | DATA } => #end EVMC_INVALID_BLOCK ... </k>
requires ADDR ==K DEPOSIT_CONTRACT_ADDRESS
andBool size(TOPICS) >Int 0
andBool {TOPICS[0]}:>Int ==Int DEPOSIT_EVENT_SIGNATURE_HASH
andBool notBool #isValidDepositEventData(DATA)

rule <k> #parseDepositRequest _ => .K ... </k> [owise]
```
### Blobs

- `#validateBlockBlobs COUNT TXIDS`: Iterates through the transactions of the current block in order, counting up total versioned hashes (blob commitments) in the block.
Expand Down Expand Up @@ -790,9 +845,13 @@ Terminates validation successfully when all conditions are met or when blob vali
syntax EthereumCommand ::= "#finalizeBlock"
| #rewardOmmers ( JSONs ) [symbol(#rewardOmmers)]
// --------------------------------------------------------------------------
rule <k> #finalizeBlock => #if Ghaswithdrawals << SCHED >> #then #finalizeWithdrawals #else .K #fi
rule <k> #finalizeBlock
=> #if Ghaswithdrawals << SCHED >> #then #finalizeWithdrawals #else .K #fi
~> #rewardOmmers(OMMERS)
~> #finalizeBlockBlobs ... </k>
~> #filterLogs 0
~> #finalizeBlockBlobs
...
</k>
<schedule> SCHED </schedule>
<ommerBlockHeaders> [ OMMERS ] </ommerBlockHeaders>
<coinbase> MINER </coinbase>
Expand Down
2 changes: 1 addition & 1 deletion kevm-pyk/src/kevm_pyk/kproj/evm-semantics/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The following codes all indicate that the VM ended execution with an exception,
| "EVMC_PRECOMPILE_FAILURE"
| "EVMC_NONCE_EXCEEDED"
| "EVMC_INVALID_BLOCK"
// -------------------------------------------------------------
// -----------------------------------------------------
rule StatusCode2String(EVMC_FAILURE) => "EVMC_FAILURE"
rule StatusCode2String(EVMC_INVALID_INSTRUCTION) => "EVMC_INVALID_INSTRUCTION"
rule StatusCode2String(EVMC_UNDEFINED_INSTRUCTION) => "EVMC_UNDEFINED_INSTRUCTION"
Expand Down
113 changes: 113 additions & 0 deletions kevm-pyk/src/kevm_pyk/kproj/evm-semantics/requests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
Implementation of Execution Layer Requests
------------------------------------------
```k
requires "serialization.md"
```

```k
module EVM-REQUESTS
imports SERIALIZATION
```

A `requests` object consists of a `request_type` byte prepended to an opaque byte array `request_data`.
The `request_data` contains zero or more encoded request objects.
```
requests = request_type ++ request_data
```
Each request type will define its own requests object with its own `request_data` format.

In order to compute the commitment, an intermediate hash list is first built by hashing all non-empty requests elements of the block requests list.
Items with empty `request_data` are excluded, i.e. the intermediate list skips requests items which contain only the `request_type` (1 byte) and nothing else.

```k
syntax Int ::= #computeRequestsHash(List) [function, symbol(#computeRequestsHash)]
// ----------------------------------------------------------------------------------
rule #computeRequestsHash(RS) => #parseHexWord(Sha256(#computeRequestsHashIntermediate(RS)))

syntax Bytes ::= #computeRequestsHashIntermediate(List) [function, symbol(#computeRequestsHashIntermediate)]
| #computeRequestsHashIntermediate(List, Bytes) [function, symbol(#computeRequestsHashIntermediateAux)]
// ----------------------------------------------------------------------------------------------------------------------
rule #computeRequestsHashIntermediate(RS) => #computeRequestsHashIntermediate(RS, .Bytes)
rule #computeRequestsHashIntermediate(.List, ACC) => ACC
rule #computeRequestsHashIntermediate(ListItem(R) RS, ACC) => #computeRequestsHashIntermediate(RS, ACC)
requires lengthBytes(R) <=Int 1
rule #computeRequestsHashIntermediate(ListItem(R) RS, ACC) => #computeRequestsHashIntermediate(RS, ACC +Bytes Sha256raw(R))
requires lengthBytes(R) >Int 1
```

Deposit Requests
----------------
The structure denoting the new deposit request consists of the following fields:

1. `pubkey: Bytes48`
2. `withdrawal_credentials: Bytes32`
3. `amount: uint64`
4. `signature: Bytes96`
5. `index: uint64`

```k
syntax Int ::= "DEPOSIT_REQUEST_TYPE" [macro]
| "DEPOSIT_EVENT_LENGTH" [macro]
| "DEPOSIT_CONTRACT_ADDRESS" [alias]
| "DEPOSIT_EVENT_SIGNATURE_HASH" [alias]
// -----------------------------------------------------
rule DEPOSIT_REQUEST_TYPE => 0
rule DEPOSIT_CONTRACT_ADDRESS => #parseAddr("0x00000000219ab540356cbb839cbe05303d7705fa")
rule DEPOSIT_EVENT_SIGNATURE_HASH => #parseWord("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
rule DEPOSIT_EVENT_LENGTH => 576

syntax Int ::= "PUBKEY_OFFSET" [macro]
| "WITHDRAWAL_CREDENTIALS_OFFSET"[macro]
| "AMOUNT_OFFSET" [macro]
| "SIGNATURE_OFFSET" [macro]
| "INDEX_OFFSET" [macro]
| "PUBKEY_SIZE" [macro]
| "WITHDRAWAL_CREDENTIALS_SIZE" [macro]
| "AMOUNT_SIZE" [macro]
| "SIGNATURE_SIZE" [macro]
| "INDEX_SIZE" [macro]
// -----------------------------------------------------
rule PUBKEY_OFFSET => 160
rule WITHDRAWAL_CREDENTIALS_OFFSET => 256
rule AMOUNT_OFFSET => 320
rule SIGNATURE_OFFSET => 384
rule INDEX_OFFSET => 512
rule PUBKEY_SIZE => 48
rule WITHDRAWAL_CREDENTIALS_SIZE => 32
rule AMOUNT_SIZE => 8
rule SIGNATURE_SIZE => 96
rule INDEX_SIZE => 8
```



```k
syntax Bytes ::= #extractDepositData ( Bytes ) [function, symbol(#extractDepositData)]
// --------------------------------------------------------------------------------------
rule #extractDepositData(DATA) => substrBytes(DATA, PUBKEY_OFFSET +Int 32, PUBKEY_OFFSET +Int 32 +Int PUBKEY_SIZE)
+Bytes substrBytes(DATA, WITHDRAWAL_CREDENTIALS_OFFSET +Int 32, WITHDRAWAL_CREDENTIALS_OFFSET +Int 32 +Int WITHDRAWAL_CREDENTIALS_SIZE)
+Bytes substrBytes(DATA, AMOUNT_OFFSET +Int 32, AMOUNT_OFFSET +Int 32 +Int AMOUNT_SIZE)
+Bytes substrBytes(DATA, SIGNATURE_OFFSET +Int 32, SIGNATURE_OFFSET +Int 32 +Int SIGNATURE_SIZE)
+Bytes substrBytes(DATA, INDEX_OFFSET +Int 32, INDEX_OFFSET +Int 32 +Int INDEX_SIZE)

syntax Bool ::= #isValidDepositEventData ( Bytes ) [function, symbol(#isValidDepositEventData), total]
// ------------------------------------------------------------------------------------------------------
rule #isValidDepositEventData(DATA) => true
requires lengthBytes(DATA) ==Int DEPOSIT_EVENT_LENGTH
andBool Bytes2Int(substrBytes(DATA, 0, 32), BE, Unsigned) ==Int PUBKEY_OFFSET
andBool Bytes2Int(substrBytes(DATA, 32, 64), BE, Unsigned) ==Int WITHDRAWAL_CREDENTIALS_OFFSET
andBool Bytes2Int(substrBytes(DATA, 64, 96), BE, Unsigned) ==Int AMOUNT_OFFSET
andBool Bytes2Int(substrBytes(DATA, 96, 128), BE, Unsigned) ==Int SIGNATURE_OFFSET
andBool Bytes2Int(substrBytes(DATA, 128, 160), BE, Unsigned) ==Int INDEX_OFFSET
andBool Bytes2Int(substrBytes(DATA, PUBKEY_OFFSET, PUBKEY_OFFSET +Int 32), BE, Unsigned) ==Int PUBKEY_SIZE
andBool Bytes2Int(substrBytes(DATA, WITHDRAWAL_CREDENTIALS_OFFSET, WITHDRAWAL_CREDENTIALS_OFFSET +Int 32), BE, Unsigned) ==Int WITHDRAWAL_CREDENTIALS_SIZE
andBool Bytes2Int(substrBytes(DATA, AMOUNT_OFFSET, AMOUNT_OFFSET +Int 32), BE, Unsigned) ==Int AMOUNT_SIZE
andBool Bytes2Int(substrBytes(DATA, SIGNATURE_OFFSET, SIGNATURE_OFFSET +Int 32), BE, Unsigned) ==Int SIGNATURE_SIZE
andBool Bytes2Int(substrBytes(DATA, INDEX_OFFSET, INDEX_OFFSET +Int 32), BE, Unsigned) ==Int INDEX_SIZE

rule #isValidDepositEventData(_) => false [owise]
```

```k
endmodule
```
8 changes: 6 additions & 2 deletions kevm-pyk/src/kevm_pyk/kproj/evm-semantics/schedule.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ module SCHEDULE
| "Ghasrejectedfirstbyte" | "Ghasprevrandao" | "Ghasmaxinitcodesize" | "Ghaspushzero"
| "Ghaswarmcoinbase" | "Ghaswithdrawals" | "Ghastransient" | "Ghasmcopy"
| "Ghasbeaconroot" | "Ghaseip6780" | "Ghasblobbasefee" | "Ghasblobhash"
| "Ghasrequests"
// --------------------------------------
```

### Schedule Constants
Expand Down Expand Up @@ -161,6 +163,7 @@ A `ScheduleConst` is a constant determined by the fee schedule.
rule [GhasbeaconrootDefault]: Ghasbeaconroot << DEFAULT >> => false
rule [Ghaseip6780Default]: Ghaseip6780 << DEFAULT >> => false
rule [GhasblobhashDefault]: Ghasblobhash << DEFAULT >> => false
rule [GhasrequestsDefault]: Ghasrequests << DEFAULT >> => false
```

### Frontier Schedule
Expand Down Expand Up @@ -438,8 +441,9 @@ A `ScheduleConst` is a constant determined by the fee schedule.
// --------------------------------------------------------------------------
rule [SCHEDCONSTPrague]: SCHEDCONST < PRAGUE > => SCHEDCONST < CANCUN >

rule [SCHEDFLAGPrague]: SCHEDFLAG << PRAGUE >> => SCHEDFLAG << CANCUN >>

rule [GhasrequestsPrague]: Ghasrequests << PRAGUE >> => true
rule [SCHEDFLAGPrague]: SCHEDFLAG << PRAGUE >> => SCHEDFLAG << CANCUN >>
requires notBool ( SCHEDFLAG ==K Ghasrequests )
```

```k
Expand Down
Loading