|
1 |
| -# builder |
| 1 | +# The Signet Block Builder |
2 | 2 |
|
3 |
| -Our sample signet builder implementation. |
| 3 | +The Builder simulates bundles and transactions against the latest chain state to create valid Signet rollup blocks and submits them to the configured host chain as an [EIP-4844 transaction](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md). |
4 | 4 |
|
5 |
| -## Development |
| 5 | +Bundles are treated as Flashbots-style bundles, meaning that the Builder should respect transaction ordering, bundle atomicity, and the specified revertability. |
6 | 6 |
|
7 |
| -This crate contains an example block builder in the Signet ecosystem. |
| 7 | +--- |
8 | 8 |
|
9 |
| -### Requirements |
| 9 | +## 🚀 System Design |
| 10 | + |
| 11 | +The Builder orchestrates a series of asynchronous actors that work together to build blocks for every assigned slot. |
| 12 | + |
| 13 | +1. **Env** - watches the latest host and rollup blocks to monitor gas rates and block updates. |
| 14 | +2. **Cache** - polls bundle and transaction caches and adds them to the cache. |
| 15 | +3. **Simulator** - simulates transactions and bundles against rollup state and block environment to build them into a cohesive block. |
| 16 | +5. **Submit** - creates a blob transaction from the built block and sends it to Ethereum L1. |
| 17 | +6. **Metrics** - records block and tx data over time. |
| 18 | + |
| 19 | +```mermaid |
| 20 | +%%{ init : { "theme" : "dark" } }%% |
| 21 | +flowchart TD |
| 22 | + %% ────────────── INITIALIZATION ────────────── |
| 23 | + A0(["Start main"]) --> A1[Init tracing & logging] |
| 24 | + A1 --> A2_BuilderConfig[Load BuilderConfig from env] |
| 25 | + |
| 26 | + %% ────────────── CORE TASK SPAWNS ────────────── |
| 27 | + subgraph Tasks_Spawned["Spawned Actors"] |
| 28 | + EnvTaskActor["🔢 Env Task"] ==block_env==> CacheSystem |
| 29 | + CacheSystem[" Cache System"] |
| 30 | + MetricsTaskActor["📏 Metrics Task"] |
| 31 | + SubmitTaskActor["📡 Submit Task "] |
| 32 | + SimulatorTaskActor["💾 Simulator Task"] |
| 33 | + Quincey["🖊️ Quincey"] |
| 34 | +
|
| 35 | + SubmitTaskActor -.block hash.-> Quincey |
| 36 | + Quincey -.block signature.-> SubmitTaskActor |
| 37 | + end |
| 38 | +
|
| 39 | + %% ────────────── CONNECTIONS & DATA FLOW ────────────── |
| 40 | + A2_BuilderConfig -.host_provider.-> MetricsTaskActor |
| 41 | + A2_BuilderConfig -.host_provider.->SubmitTaskActor |
| 42 | + A2_BuilderConfig -.ru_provider.-> SimulatorTaskActor |
| 43 | + A2_BuilderConfig -.ru_provider.-> EnvTaskActor |
10 | 44 |
|
11 |
| -- Rust 1.81.0 |
12 |
| -- Cargo [Lambda](https://www.cargo-lambda.info/) |
13 |
| -- AWS CLI and credentials |
14 |
| - |
15 |
| -### Environment |
16 |
| - |
17 |
| -The following environment variables are exposed to configure the Builder: |
18 |
| - |
19 |
| -```bash |
20 |
| -# Builder Configs |
21 |
| -HOST_CHAIN_ID="17000" # Holesky Testnet |
22 |
| -RU_CHAIN_ID="17001" |
23 |
| -HOST_RPC_URL="http://host.url.here" |
24 |
| -# trailing slash is required |
25 |
| -TX_BROADCAST_URLS="http://tx.broadcast.url.here/,https://additional.url.here/" |
26 |
| -ZENITH_ADDRESS="ZENITH_ADDRESS_HERE" |
27 |
| -QUINCEY_URL="http://signer.url.here" |
28 |
| -BUILDER_PORT="8080" |
29 |
| -BUILDER_KEY="YOUR_BUILDER_KEY_HERE" |
30 |
| -INCOMING_TRANSACTIONS_BUFFER="10" |
31 |
| -BLOCK_CONFIRMATION_BUFFER="10" |
32 |
| -BUILDER_REWARDS_ADDRESS="BUILDER_REWARDS_ADDRESS_HERE" |
33 |
| -ROLLUP_BLOCK_GAS_LIMIT="30000000" |
34 |
| -# Transaction Pool Configs |
35 |
| -TX_POOL_URL="http://pool.url.here/" # trailing slash is required |
36 |
| -TX_POOL_POLL_INTERVAL="5" # seconds |
37 |
| -TX_POOL_CACHE_DURATION="600" # seconds |
| 45 | + A3["📥 Transactions & |
| 46 | + 📦 Bundles"] --> CacheSystem |
| 47 | +
|
| 48 | + EnvTaskActor ==block_env==> SimulatorTaskActor |
| 49 | + CacheSystem ==sim_cache ==> SimulatorTaskActor |
| 50 | + SubmitTaskActor ==tx receipt==> MetricsTaskActor |
| 51 | + SimulatorTaskActor ==built block==> SubmitTaskActor |
| 52 | +
|
| 53 | + SubmitTaskActor ==>|"signet block (blob tx)"| C1["⛓️ Ethereum L1"] |
38 | 54 | ```
|
39 | 55 |
|
40 |
| -## API |
| 56 | +The block building loop waits until a new block has been received, and then kicks off the next attempt. |
| 57 | + |
| 58 | +When the Builder receives a new block, it takes a reference to the transaction cache, calculates a simulation deadline for the current slot with a buffer of 1.5 seconds, and begins constructing a block for the current slot. |
| 59 | + |
| 60 | +Transactions enter through the cache, and then they're sent to the simulator, where they're run against the latest chain state and block environment. If they're successfully applied, they're added to the block. If a transaction fails to be applied, it is simply ignored. |
| 61 | + |
| 62 | +When the deadline is reached, the simulator is stopped, and all open simulation threads and cancelled. The block is then bundled with the block environment and the previous host header that it was simulated against, and passes all three along to the submit task. |
41 | 63 |
|
42 |
| -### SignRequest |
| 64 | +If no transactions in the cache are valid and the resulting block is empty, the submit task will ignore it. |
43 | 65 |
|
44 |
| -Sign request example payload: |
| 66 | +Finally, if it's non-empty, the submit task attempts to get a signature for the block, and if it fails due to a 403 error, it will skip the current slot and begin waiting for the next block. |
45 | 67 |
|
46 |
| -```json |
47 |
| -{ |
48 |
| - "hostBlockNumber": "0x0", |
49 |
| - "hostChainId": "0x1", |
50 |
| - "ruChainId": "0x2", |
51 |
| - "gasLimit": "0x5", |
52 |
| - "ruRewardAddress": "0x0606060606060606060606060606060606060606", |
53 |
| - "contents": "0x0707070707070707070707070707070707070707070707070707070707070707" |
54 |
| -} |
| 68 | +--- |
| 69 | + |
| 70 | +## ⚙️ Configuration |
| 71 | + |
| 72 | +The Builder is configured via environment variables. The following values are supported for configuration. |
| 73 | + |
| 74 | +| Key | Required | Description | |
| 75 | +| ------------------------- | -------- | ------------------------------------------------------------------- | |
| 76 | +| `HOST_CHAIN_ID` | Yes | Host-chain ID (e.g. `3151908`) | |
| 77 | +| `RU_CHAIN_ID` | Yes | Rollup-chain ID (e.g. `14174`) | |
| 78 | +| `TX_POOL_URL` | Yes | Transaction pool URL (must end with `/`) | |
| 79 | +| `HOST_RPC_URL` | Yes | RPC endpoint for the host chain | |
| 80 | +| `RU_RPC_URL` | Yes | RPC endpoint for the rollup chain | |
| 81 | +| `TX_BROADCAST_URLS` | No | Additional endpoints for blob txs (comma-separated, slash required) | |
| 82 | +| `ZENITH_ADDRESS` | Yes | Zenith contract address | |
| 83 | +| `BUILDER_HELPER_ADDRESS` | Yes | Builder helper contract address | |
| 84 | +| `QUINCEY_URL` | Yes | Remote sequencer signing endpoint | |
| 85 | +| `BUILDER_PORT` | Yes | HTTP port for the Builder (default: `8080`) | |
| 86 | +| `SEQUENCER_KEY` | Yes | AWS KMS key ID _or_ local private key for sequencer signing | |
| 87 | +| `BUILDER_KEY` | Yes | AWS KMS key ID _or_ local private key for builder signing | |
| 88 | +| `BUILDER_REWARDS_ADDRESS` | Yes | Address receiving builder rewards | |
| 89 | +| `ROLLUP_BLOCK_GAS_LIMIT` | No | Override for block gas limit | |
| 90 | +| `CONCURRENCY_LIMIT` | Yes | Max concurrent tasks the simulator uses | |
| 91 | +| `SLOT_OFFSET` | Yes | Slot timing offset in seconds | |
| 92 | +| `SLOT_DURATION` | Yes | Slot duration in seconds | |
| 93 | +| `START_TIMESTAMP` | Yes | UNIX timestamp for slot 0 | |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## 💾 EVM Behavior |
| 98 | + |
| 99 | +### 🗿 Inherited Header Values |
| 100 | + |
| 101 | +`PREVRANDAO` is set to a random byte string for each block. |
| 102 | + |
| 103 | +```rust |
| 104 | +// `src/tasks/env.rs` |
| 105 | +prevrandao: Some(B256::random()), |
55 | 106 | ```
|
| 107 | + |
| 108 | +`TIMESTAMP` - Block timestamps are set to the same value as the current Ethereum block. |
| 109 | + |
| 110 | +Blob gas values `excess_blob_gas` and `blob_gasprice` are also set to 0 for all Signet blocks. |
| 111 | + |
| 112 | +### 🔢 Disabled Opcodes |
| 113 | + |
| 114 | +`BLOBHASH` - EIP-4844 is not supported on Signet. |
| 115 | +`BLOBBASEFEE` - EIP4844 is not supported. |
| 116 | + |
| 117 | +## ⛽ Transaction Submission |
| 118 | + |
| 119 | +When a completed, non-empty Signet block is received by the Submit task, it prepares the block data into a blob transaction and submits it to the network. |
| 120 | + |
| 121 | +If it fails, it will retry up to 3 times with a 12.5% bump on each retry. |
| 122 | + |
| 123 | +The previous header's basefee is tracked through the build loop and used for gas estimation purposes in the Submit Task. |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## 📤 Transaction Sender |
| 128 | + |
| 129 | +A binary (`bin/submit-transaction.rs`) for continously sending very small transactions for testing block construction. |
| 130 | + |
| 131 | +The following values are available for configuring the transaction sender: |
| 132 | + |
| 133 | +| Key | Required | Description | |
| 134 | +| ------------------- | -------- | ------------------------------------------------ | |
| 135 | +| `RPC_URL` | Yes | RPC endpoint used for sending the transaction | |
| 136 | +| `RECIPIENT_ADDRESS` | Yes | Address to which the transaction is sent | |
| 137 | +| `SLEEP_TIME` | No | Optional delay (in seconds) between transactions | |
| 138 | +| `SIGNER_CHAIN_ID` | Yes | Chain ID used for signing | |
| 139 | +| `SIGNER_KEY` | Yes | Signing key used to sign the transaction | |
| 140 | + |
| 141 | +The transaction submitter is located at `bin/submit_transaction.rs`. |
| 142 | + |
| 143 | +Run the transaction submitter with `cargo run --bin transaction-submitter` |
| 144 | + |
| 145 | +--- |
| 146 | + |
| 147 | +## 🛠️ Development |
| 148 | + |
| 149 | +### Requirements |
| 150 | + |
| 151 | +- **Rust** ≥ 1.85 |
| 152 | +- **AWS CLI** |
| 153 | +- A private key or AWS KMS key for signing transactions |
| 154 | + |
| 155 | +--- |
| 156 | + |
| 157 | +## ✅ Testing |
| 158 | + |
| 159 | +1. Build the Docker image: |
| 160 | + ```bash |
| 161 | + docker build -t builder:latest . |
| 162 | + ``` |
| 163 | +2. Push to your container registry: |
| 164 | + ```bash |
| 165 | + docker push <registry>/builder:latest |
| 166 | + ``` |
| 167 | +3. Update your deployment manifests with the new image. |
| 168 | +4. Verify expected behavior in your target network. |
| 169 | + - This should typically include sending a test transaction and verifying it is simulated and built into a block. |
| 170 | + |
| 171 | +## 🪪 License |
| 172 | + |
| 173 | +This project is licensed under the [MIT License](https://opensource.org/licenses/MIT). |
0 commit comments