feat: add proposer selection #228
Conversation
📝 WalkthroughWalkthroughThis PR introduces a proposer-control precompile for EVM-based proposer rotation. It spans the precompile contract, EVM factory integration, chainspec configuration, executor wiring, a new JSON-RPC API, node binary registration, and supporting documentation. All changes work together to enable execution-owned proposer management. ChangesProposer Rotation Precompile
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bin/ev-dev/src/main.rs`:
- Around line 305-315: The TUI startup path fails to register the proposer RPC,
so evolve_getNextProposer is unavailable when run_with_tui is used; fix by
creating and merging the proposer API in the TUI branch the same way as the
non-TUI branch: construct EvolvePayloadBuilderConfig (or reuse proposer_cfg),
derive initial_next_proposer, instantiate EvolveProposerApiImpl (proposer_api)
with ctx.provider() and the initial value, then call
ctx.modules.merge_configured(proposer_api.into_rpc()) alongside merging
evolve_txpool in the run_with_tui code path so the proposer RPC is registered
for TUI mode as well.
In `@crates/ev-precompiles/README.md`:
- Around line 202-204: The fenced code block containing the address
0x000000000000000000000000000000000000f101 should include a language tag to
satisfy MD040; update the fence opening from ``` to ```text so the block is
fenced as text (locate the fenced block containing the address string and add
the language tag).
- Around line 208-252: The README docs and example ABI need to use bytes32 for
proposer values to match the precompile: update the IProposerControl interface
declaration (replace address with bytes32 for nextProposer() and
setNextProposer(bytes32)), change the JSON config keys (proposerControlAdmin
stays address but initialNextProposer must be a bytes32 value), and update all
example CLI commands and cast calls to pass/expect a bytes32 (and the
corresponding calldata/signature) instead of an address; verify references to
nextProposer and setNextProposer in the prose reflect bytes32 semantics to avoid
mismatched calldata with crates/ev-precompiles/src/proposer.rs.
In `@docs/adr/ADR-0004-proposer-rotation-precompile.md`:
- Around line 93-99: The ADR currently declares proposer identity as address (in
the IProposerControl interface and related prose) but the implementation uses
bytes32 proposer IDs; update the ADR to use bytes32 for nextProposer() and
setNextProposer(bytes32 proposer) in the IProposerControl symbol references and
change all prose/semantics that mention “zero address” to reference the bytes32
zero value (bytes32::ZERO) instead; apply the same change to the other affected
sections noted (around the prose covering lines 134-156 and 166-167) so the API
signatures and semantics match the implemented bytes32 proposer identity while
leaving admin() as an address if implementation uses address for admin.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 74e3a819-0c8e-4041-a931-d5e6a76d39e8
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
bin/ev-dev/src/main.rsbin/ev-reth/src/main.rscrates/ev-precompiles/README.mdcrates/ev-precompiles/src/lib.rscrates/ev-precompiles/src/proposer.rscrates/ev-revm/src/factory.rscrates/ev-revm/src/lib.rscrates/node/Cargo.tomlcrates/node/src/config.rscrates/node/src/executor.rscrates/node/src/lib.rscrates/node/src/payload_service.rscrates/node/src/proposer_rpc.rscrates/tests/src/common.rsdocs/adr/ADR-0004-proposer-rotation-precompile.md
| let proposer_cfg = | ||
| EvolvePayloadBuilderConfig::from_chain_spec(ctx.config().chain.as_ref())?; | ||
| let initial_next_proposer = proposer_cfg | ||
| .proposer_control_precompile_settings() | ||
| .map(|(_, _, initial_next_proposer)| initial_next_proposer) | ||
| .unwrap_or_default(); | ||
| let proposer_api = | ||
| EvolveProposerApiImpl::new(ctx.provider().clone(), initial_next_proposer); | ||
| ctx.modules.merge_configured(evolve_txpool.into_rpc())?; | ||
| ctx.modules.merge_configured(proposer_api.into_rpc())?; | ||
| Ok(()) |
There was a problem hiding this comment.
Proposer RPC is not registered in TUI mode.
You register proposer RPC in the non-TUI startup path, but Line 380-385 (run_with_tui) still only merges txpool. This makes evolve_getNextProposer unavailable whenever --tui is used.
Suggested fix
@@
let _handle = builder
.node(EvolveNode::new())
.extend_rpc_modules(move |ctx| {
let evolve_cfg = EvolveConfig::default();
let evolve_txpool =
EvolveTxpoolApiImpl::new(ctx.pool().clone(), evolve_cfg.max_txpool_bytes);
+ let proposer_cfg =
+ EvolvePayloadBuilderConfig::from_chain_spec(ctx.config().chain.as_ref())?;
+ let initial_next_proposer = proposer_cfg
+ .proposer_control_precompile_settings()
+ .map(|(_, _, initial_next_proposer)| initial_next_proposer)
+ .unwrap_or_default();
+ let proposer_api =
+ EvolveProposerApiImpl::new(ctx.provider().clone(), initial_next_proposer);
ctx.modules.merge_configured(evolve_txpool.into_rpc())?;
+ ctx.modules.merge_configured(proposer_api.into_rpc())?;
Ok(())
})
.launch_with_debug_capabilities()
.await?;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/ev-dev/src/main.rs` around lines 305 - 315, The TUI startup path fails to
register the proposer RPC, so evolve_getNextProposer is unavailable when
run_with_tui is used; fix by creating and merging the proposer API in the TUI
branch the same way as the non-TUI branch: construct EvolvePayloadBuilderConfig
(or reuse proposer_cfg), derive initial_next_proposer, instantiate
EvolveProposerApiImpl (proposer_api) with ctx.provider() and the initial value,
then call ctx.modules.merge_configured(proposer_api.into_rpc()) alongside
merging evolve_txpool in the run_with_tui code path so the proposer RPC is
registered for TUI mode as well.
| ``` | ||
| 0x000000000000000000000000000000000000f101 | ||
| ``` |
There was a problem hiding this comment.
Add a language tag to the new fenced block.
Line 202 opens a fence without a language (```), which triggers MD040. Use ```text for the address block.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 202-202: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/ev-precompiles/README.md` around lines 202 - 204, The fenced code
block containing the address 0x000000000000000000000000000000000000f101 should
include a language tag to satisfy MD040; update the fence opening from ``` to
```text so the block is fenced as text (locate the fenced block containing the
address string and add the language tag).
Source: Linters/SAST tools
| ```solidity | ||
| interface IProposerControl { | ||
| function nextProposer() external view returns (address); | ||
| function setNextProposer(address proposer) external; | ||
| function admin() external view returns (address); | ||
| } | ||
| ``` | ||
|
|
||
| ### Configuration | ||
|
|
||
| ```json | ||
| { | ||
| "config": { | ||
| "evolve": { | ||
| "proposerControlAdmin": "0x1234567890123456789012345678901234567890", | ||
| "proposerControlActivationHeight": 0, | ||
| "initialNextProposer": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| For existing chains, set `proposerControlActivationHeight` to a future block and upgrade all nodes | ||
| before that height. `initialNextProposer` should be the currently active proposer so reads are stable | ||
| before the first rotation transaction. | ||
|
|
||
| ### Operations | ||
|
|
||
| The configured admin rotates the next proposer with: | ||
|
|
||
| ```bash | ||
| cast send --rpc-url $RPC_URL --private-key $ADMIN_KEY \ | ||
| 0x000000000000000000000000000000000000f101 \ | ||
| "setNextProposer(address)" 0xNEXT_PROPOSER | ||
| ``` | ||
|
|
||
| The stored proposer can be read through either the precompile ABI or ev-reth's convenience RPC: | ||
|
|
||
| ```bash | ||
| cast call --rpc-url $RPC_URL \ | ||
| 0x000000000000000000000000000000000000f101 \ | ||
| "nextProposer()(address)" | ||
|
|
||
| cast rpc --rpc-url $RPC_URL evolve_getNextProposer latest | ||
| ``` |
There was a problem hiding this comment.
Update proposer ABI/docs to bytes32 to match the implemented precompile.
Lines 210-212, 241-242, and 249 currently document address, but crates/ev-precompiles/src/proposer.rs implements nextProposer()/setNextProposer as bytes32. Following the current README will produce incorrect calldata/signatures and failed calls.
Suggested doc diff
interface IProposerControl {
- function nextProposer() external view returns (address);
- function setNextProposer(address proposer) external;
+ function nextProposer() external view returns (bytes32);
+ function setNextProposer(bytes32 proposer) external;
function admin() external view returns (address);
}
-cast send ... "setNextProposer(address)" 0xNEXT_PROPOSER
+cast send ... "setNextProposer(bytes32)" 0x<64-hex-bytes>
-cast call ... "nextProposer()(address)"
+cast call ... "nextProposer()(bytes32)"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ```solidity | |
| interface IProposerControl { | |
| function nextProposer() external view returns (address); | |
| function setNextProposer(address proposer) external; | |
| function admin() external view returns (address); | |
| } | |
| ``` | |
| ### Configuration | |
| ```json | |
| { | |
| "config": { | |
| "evolve": { | |
| "proposerControlAdmin": "0x1234567890123456789012345678901234567890", | |
| "proposerControlActivationHeight": 0, | |
| "initialNextProposer": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" | |
| } | |
| } | |
| } | |
| ``` | |
| For existing chains, set `proposerControlActivationHeight` to a future block and upgrade all nodes | |
| before that height. `initialNextProposer` should be the currently active proposer so reads are stable | |
| before the first rotation transaction. | |
| ### Operations | |
| The configured admin rotates the next proposer with: | |
| ```bash | |
| cast send --rpc-url $RPC_URL --private-key $ADMIN_KEY \ | |
| 0x000000000000000000000000000000000000f101 \ | |
| "setNextProposer(address)" 0xNEXT_PROPOSER | |
| ``` | |
| The stored proposer can be read through either the precompile ABI or ev-reth's convenience RPC: | |
| ```bash | |
| cast call --rpc-url $RPC_URL \ | |
| 0x000000000000000000000000000000000000f101 \ | |
| "nextProposer()(address)" | |
| cast rpc --rpc-url $RPC_URL evolve_getNextProposer latest | |
| ``` |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/ev-precompiles/README.md` around lines 208 - 252, The README docs and
example ABI need to use bytes32 for proposer values to match the precompile:
update the IProposerControl interface declaration (replace address with bytes32
for nextProposer() and setNextProposer(bytes32)), change the JSON config keys
(proposerControlAdmin stays address but initialNextProposer must be a bytes32
value), and update all example CLI commands and cast calls to pass/expect a
bytes32 (and the corresponding calldata/signature) instead of an address; verify
references to nextProposer and setNextProposer in the prose reflect bytes32
semantics to avoid mismatched calldata with
crates/ev-precompiles/src/proposer.rs.
| ```solidity | ||
| interface IProposerControl { | ||
| function nextProposer() external view returns (address); | ||
| function setNextProposer(address proposer) external; | ||
| function admin() external view returns (address); | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Bring ADR API and semantics in sync with implemented bytes32 proposer identity.
Line 95-97 and related semantics (Lines 134-156, 166-167) still specify address/“zero address”, but the implementation stores bytes32 proposer IDs and validates zero as bytes32::ZERO. This mismatch can mislead downstream adapters and tooling.
Also applies to: 134-156, 166-167
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/adr/ADR-0004-proposer-rotation-precompile.md` around lines 93 - 99, The
ADR currently declares proposer identity as address (in the IProposerControl
interface and related prose) but the implementation uses bytes32 proposer IDs;
update the ADR to use bytes32 for nextProposer() and setNextProposer(bytes32
proposer) in the IProposerControl symbol references and change all
prose/semantics that mention “zero address” to reference the bytes32 zero value
(bytes32::ZERO) instead; apply the same change to the other affected sections
noted (around the prose covering lines 134-156 and 166-167) so the API
signatures and semantics match the implemented bytes32 proposer identity while
leaving admin() as an address if implementation uses address for admin.
Description
this pr adds proposer selection to ev-reth based on evstack/ev-node#3282
Type of Change
Related Issues
Fixes #(issue)
Checklist
Testing
Additional Notes
Summary by CodeRabbit
New Features
Documentation