Skip to content

Commit 59d1fc9

Browse files
authored
docs: update readme (#103)
# update readme - General cleanup and polish of the builder readme - Removes unused env vars and updates out of date values - Adds mermaid architecture diagram - Removes unused environment values `block_confirmation_buffer` Closes ENG-908
1 parent 15eb80e commit 59d1fc9

File tree

3 files changed

+189
-72
lines changed

3 files changed

+189
-72
lines changed

README.md

Lines changed: 162 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,173 @@
1-
# builder
1+
# The Signet Block Builder
22

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).
44

5-
## Development
5+
Bundles are treated as Flashbots-style bundles, meaning that the Builder should respect transaction ordering, bundle atomicity, and the specified revertability.
66

7-
This crate contains an example block builder in the Signet ecosystem.
7+
---
88

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
1044
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"]
3854
```
3955

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.
4163

42-
### SignRequest
64+
If no transactions in the cache are valid and the resulting block is empty, the submit task will ignore it.
4365

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.
4567

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()),
55106
```
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).

src/config.rs

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,47 +55,65 @@ pub type HostProvider = FillProvider<
5555
/// chain.
5656
#[derive(Debug, Clone, FromEnv)]
5757
pub struct BuilderConfig {
58-
/// The chain ID of the host chain
58+
/// The chain ID of the host chain.
5959
#[from_env(var = "HOST_CHAIN_ID", desc = "The chain ID of the host chain")]
6060
pub host_chain_id: u64,
61-
/// The chain ID of the rollup chain
61+
62+
/// The chain ID of the rollup chain.
6263
#[from_env(var = "RU_CHAIN_ID", desc = "The chain ID of the rollup chain")]
6364
pub ru_chain_id: u64,
65+
6466
/// URL for Host RPC node.
6567
#[from_env(var = "HOST_RPC_URL", desc = "URL for Host RPC node", infallible)]
6668
pub host_rpc_url: Cow<'static, str>,
69+
6770
/// URL for the Rollup RPC node.
6871
#[from_env(var = "ROLLUP_RPC_URL", desc = "URL for Rollup RPC node", infallible)]
6972
pub ru_rpc_url: Cow<'static, str>,
70-
/// Additional RPC URLs to which to broadcast transactions.
71-
/// NOTE: should not include the host_rpc_url value
73+
74+
/// URL of the tx pool to poll for incoming transactions.
75+
#[from_env(
76+
var = "TX_POOL_URL",
77+
desc = "URL of the tx pool to poll for incoming transactions",
78+
infallible
79+
)]
80+
pub tx_pool_url: Cow<'static, str>,
81+
82+
/// Additional RPC URLs to which the builder should broadcast transactions.
83+
/// * Should not include the `HOST_RPC_URL` value, as that is already sent to by default.
84+
/// * Setting this can incur `already known` errors.
7285
#[from_env(
7386
var = "TX_BROADCAST_URLS",
7487
desc = "Additional RPC URLs to which the builder broadcasts transactions",
7588
infallible,
7689
optional
7790
)]
7891
pub tx_broadcast_urls: Vec<Cow<'static, str>>,
79-
/// address of the Zenith contract on Host.
92+
93+
/// Address of the Zenith contract on Host.
8094
#[from_env(var = "ZENITH_ADDRESS", desc = "address of the Zenith contract on Host")]
8195
pub zenith_address: Address,
82-
/// address of the Builder Helper contract on Host.
96+
97+
/// Address of the Builder Helper contract on Host.
8398
#[from_env(
8499
var = "BUILDER_HELPER_ADDRESS",
85100
desc = "address of the Builder Helper contract on Host"
86101
)]
87102
pub builder_helper_address: Address,
103+
88104
/// URL for remote Quincey Sequencer server to sign blocks.
89-
/// Disregarded if a sequencer_signer is configured.
105+
/// NB: Disregarded if a sequencer_signer is configured.
90106
#[from_env(
91107
var = "QUINCEY_URL",
92108
desc = "URL for remote Quincey Sequencer server to sign blocks",
93109
infallible
94110
)]
95111
pub quincey_url: Cow<'static, str>,
112+
96113
/// Port for the Builder server.
97114
#[from_env(var = "BUILDER_PORT", desc = "Port for the Builder server")]
98115
pub builder_port: u16,
116+
99117
/// Key to access Sequencer Wallet - AWS Key ID _OR_ local private key.
100118
/// Set IFF using local Sequencer signing instead of remote Quincey signing.
101119
#[from_env(
@@ -105,43 +123,26 @@ pub struct BuilderConfig {
105123
optional
106124
)]
107125
pub sequencer_key: Option<String>,
126+
108127
/// Key to access Builder transaction submission wallet - AWS Key ID _OR_ local private key.
109128
#[from_env(
110129
var = "BUILDER_KEY",
111130
desc = "Key to access Builder transaction submission wallet - AWS Key ID _OR_ local private key",
112131
infallible
113132
)]
114133
pub builder_key: String,
115-
/// Buffer in seconds in which the `submitBlock` transaction must confirm on the Host chain.
116-
#[from_env(
117-
var = "BLOCK_CONFIRMATION_BUFFER",
118-
desc = "Buffer in seconds in which the `submitBlock` transaction must confirm on the Host chain"
119-
)]
120-
pub block_confirmation_buffer: u64,
121134

122135
/// Address on Rollup to which Builder will receive user transaction fees.
123136
#[from_env(
124137
var = "BUILDER_REWARDS_ADDRESS",
125138
desc = "Address on Rollup to which Builder will receive user transaction fees"
126139
)]
127140
pub builder_rewards_address: Address,
141+
128142
/// Gas limit for RU block.
129143
/// NOTE: a "smart" builder would determine this programmatically by simulating the block.
130144
#[from_env(var = "ROLLUP_BLOCK_GAS_LIMIT", desc = "Gas limit for RU block")]
131145
pub rollup_block_gas_limit: u64,
132-
/// URL of the tx pool to poll for incoming transactions.
133-
#[from_env(
134-
var = "TX_POOL_URL",
135-
desc = "URL of the tx pool to poll for incoming transactions",
136-
infallible
137-
)]
138-
pub tx_pool_url: Cow<'static, str>,
139-
/// Duration in seconds transactions can live in the tx-pool cache.
140-
#[from_env(
141-
var = "TX_POOL_CACHE_DURATION",
142-
desc = "Duration in seconds transactions can live in the tx-pool cache"
143-
)]
144-
pub tx_pool_cache_duration: u64,
145146

146147
/// Oauth2 configuration for the builder to connect to init4 services.
147148
pub oauth: OAuthConfig,

src/test_utils.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@ pub fn setup_test_config() -> Result<BuilderConfig> {
2929
builder_port: 8080,
3030
sequencer_key: None,
3131
builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(),
32-
block_confirmation_buffer: 1,
3332
builder_rewards_address: Address::default(),
3433
rollup_block_gas_limit: 3_000_000_000,
3534
tx_pool_url: "http://localhost:9000/".into(),
36-
tx_pool_cache_duration: 5,
3735
oauth: OAuthConfig {
3836
oauth_client_id: "some_client_id".into(),
3937
oauth_client_secret: "some_client_secret".into(),

0 commit comments

Comments
 (0)