Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
add evm execution
  • Loading branch information
tac0turtle
tac0turtle committed May 5, 2025
commit e46ac799e8216c3ef6cc931fa07f3c25d7c08c5a
122 changes: 122 additions & 0 deletions execution/evm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Go Execution EVM

This repository implements the `execution.Executor` interface from `github.com/rollkit/rollkit/core/execution` (currently on feature branch `feature/exec_api`). It provides a pure Engine API-based execution client for Rollkit.

## PureEngineClient Implementation

The `PureEngineClient` is a 100% Engine API compatible implementation of the `execution.Executor` interface. It connects to an Ethereum execution client (like Reth) and uses both the Engine API and standard Ethereum JSON-RPC API to execute transactions.
Comment thread
tac0turtle marked this conversation as resolved.
Outdated

### Genesis and initial height

In the context of the EVM, the genesis block is designated as a unique block with the block number `0`.
To ensure compatibility with both rollkit and the EVM, the only permissible initial height is `1`.
During `InitChain` EVM genesis block is acknowledged (at height 0), and empty block is created (at height 1).
This approach ensures that the block numbers between the EVM and rollkit remain consistent and synchronized.

### Genesis Requirements

Since the `PureEngineClient` relies on the Engine API, the genesis configuration must properly enable it with the correct hardfork settings:

1. The genesis file must include post-merge hardfork configurations
2. `terminalTotalDifficulty` must be set to 0
3. `terminalTotalDifficultyPassed` must be set to true
4. Hardforks like `mergeNetsplitBlock`, `shanghaiTime`, and `cancunTime` should be properly configured

Example of required genesis configuration:

```json
{
"config": {
"chainId": 1234,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"mergeNetsplitBlock": 0,
"terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true,
"shanghaiTime": 0,
"cancunTime": 0
}
}
```

Without these settings, the Engine API will not be available, and the `PureEngineClient` will not function correctly.

### PayloadID Storage

The `PureEngineClient` maintains the `payloadID` between calls:

1. During `InitChain`, a payload ID is obtained from the Engine API via `engine_forkchoiceUpdatedV3`
2. This payload ID is stored in the client instance as `c.payloadID`
3. The stored payload ID is used in subsequent calls to `GetTxs` to retrieve the current execution payload
4. After each `ExecuteTxs` call, a new payload ID is obtained and stored for the next block

### Payload as First Transaction

The `PureEngineClient` implements a unique approach to transaction execution:

1. In `GetTxs`, the entire execution payload is serialized to JSON and returned as the first transaction
2. In `ExecuteTxs`, this first transaction is deserialized back into an execution payload
3. The remaining transactions are added to the payload's transaction list
4. The complete payload is then submitted to the execution client via `engine_newPayloadV3`

This approach ensures that:

- The execution payload structure is preserved between calls
- All execution happens within the EVM
- It's not possible to create a payload outside of the EVM
- Transactions cannot be selected or ordered outside of the EVM

### How Eth API is Used

The `PureEngineClient` uses the standard Ethereum JSON-RPC API for:

1. Retrieving block information (via `HeaderByNumber`)
2. Reading the genesis block hash and state root
3. Getting gas limits and other block parameters

This allows the client to interact with the execution layer for read operations while using the Engine API for write operations.

## Deployment Architecture

```mermaid
graph LR
subgraph Rollkit Binary
RollkitCore[Rollkit Core]
PureEngineClient[PureEngineClient]
end

%% Connections
RollkitCore --> PureEngineClient
PureEngineClient -->|Engine API| Reth
PureEngineClient -->|Eth API| Reth

```

## Development and Testing

### Running Reth in Docker

```bash
cd docker
docker compose up -d
```

### Reading Genesis Information

If you've modified the genesis file, you can read the genesis hash and state root using the Ethereum JSON-RPC API:

```bash
# Get genesis block hash
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0", false],"id":1}' http://localhost:8545 | jq -r '.result.hash'

# Get genesis state root
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0", false],"id":1}' http://localhost:8545 | jq -r '.result.stateRoot'
```
39 changes: 39 additions & 0 deletions execution/evm/docker/chain/genesis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"config": {
"chainId": 1234,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"mergeNetsplitBlock": 0,
"terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true,
"shanghaiTime": 0,
"cancunTime": 0
},
"nonce": "0x0",
"timestamp": "0x0",
"extraData": "0x",
"gasLimit": "0x1c9c38000",
"difficulty": "0x0",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0xd143C405751162d0F96bEE2eB5eb9C61882a736E": {
"balance": "0x4a47e3c12448f4ad000000"
},
"0x944fDcD1c868E3cC566C78023CcB38A32cDA836E": {
"balance": "0x4a47e3c12448f4ad000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"baseFeePerGas": "0x3b9aca00"
}
60 changes: 60 additions & 0 deletions execution/evm/docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: "reth"

services:
jwt-init:
container_name: jwt-init
image: alpine:3.19
volumes:
- ./jwttoken:/jwt
healthcheck:
test: ["CMD", "test", "-f", "/jwt/jwt.hex"]
interval: 5s
timeout: 5s
retries: 3
command: >
/bin/sh -c "mkdir -p /jwt &&
if [ ! -f /jwt/jwt.hex ]; then
apk add --no-cache openssl &&
openssl rand -hex 32 | tr -d '\n' > /jwt/jwt.hex;
fi"

reth:
container_name: reth
restart: unless-stopped
image: ghcr.io/paradigmxyz/reth:v1.2.1
depends_on:
jwt-init:
condition: service_completed_successfully
ports:
- "9001:9001" # metrics
- "30303:30303" # eth/66 peering
- "8545:8545" # rpc
- "8551:8551" # engine
volumes:
- ./chain:/root/chain:ro
- ./jwttoken:/root/jwt:ro
- logs:/root/logs
entrypoint: /bin/sh -c
command:
- |
reth node \
--chain /root/chain/genesis.json \
--metrics 0.0.0.0:9001 \
--log.file.directory /root/logs \
--authrpc.addr 0.0.0.0 \
--authrpc.port 8551 \
--authrpc.jwtsecret /root/jwt/jwt.hex \
--http --http.addr 0.0.0.0 --http.port 8545 \
--http.api "eth,net,web3,txpool" \
--disable-discovery \
--txpool.pending-max-count 200000 \
--txpool.pending-max-size 200 \
--txpool.queued-max-count 200000 \
--txpool.queued-max-size 200 \
--txpool.max-account-slots 2048 \
--txpool.max-new-txns 2048 \
--txpool.additional-validation-tasks 16

volumes:
logs:
driver: local
1 change: 1 addition & 0 deletions execution/evm/docker/jwttoken/jwt.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7957ce6dc4d31655a5a36069d08198e3a8f8da92bdc895779c72a2748f387612
Comment thread
tac0turtle marked this conversation as resolved.
Outdated
Loading