-
Notifications
You must be signed in to change notification settings - Fork 11
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
Initial implementation #1
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
13a733d
cargo init + add reth dependency - failing to build
ckoopmann 8434417
Copy paste ci lint workflow from reth repo
ckoopmann 1b95f8f
Copy example for CLI extension
ckoopmann 4fb5cac
Move Cli extension and rpc implementation to separate modules
ckoopmann 30e0ccb
Add implementation (not compiling)
ckoopmann 538c563
Fix build errors
ckoopmann c709197
Change fork branch and rename MyRethCli to ValidationCli
ckoopmann aec07a8
Cargo fmt
ckoopmann fbc4789
Add test rpc payload
ckoopmann 72cf18b
Add README
ckoopmann 2964684
fix test data
ckoopmann 4397780
Extend README
ckoopmann 6a6c91a
Add Disclaimer
ckoopmann 6805c59
Remove references to txpool example
ckoopmann c3acafa
Change heading of README
ckoopmann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
on: | ||
pull_request: | ||
merge_group: | ||
push: | ||
branches: [main] | ||
|
||
env: | ||
RUSTFLAGS: -D warnings | ||
CARGO_TERM_COLOR: always | ||
|
||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} | ||
cancel-in-progress: true | ||
|
||
name: ci | ||
jobs: | ||
lint: | ||
name: code lint | ||
runs-on: ubuntu-20.04 | ||
steps: | ||
- name: Checkout sources | ||
uses: actions/checkout@v3 | ||
- name: Install toolchain | ||
uses: dtolnay/rust-toolchain@nightly | ||
with: | ||
components: rustfmt, clippy | ||
- uses: Swatinem/rust-cache@v2 | ||
with: | ||
cache-on-failure: true | ||
|
||
- name: cargo check | ||
uses: actions-rs/cargo@v1 | ||
with: | ||
command: check | ||
args: --all --all-features --benches --tests | ||
|
||
- name: cargo fmt | ||
uses: actions-rs/cargo@v1 | ||
with: | ||
command: fmt | ||
args: --all --check | ||
|
||
- name: cargo clippy | ||
uses: actions-rs/cargo@v1 | ||
with: | ||
command: clippy | ||
args: --all --all-features --benches --tests | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "reth-block-validator" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
async-trait = "0.1.73" | ||
clap = "4.4.5" | ||
derivative = "2.2.0" | ||
eyre = "0.6.8" | ||
jsonrpsee = "0.20.1" | ||
reth = { git = "https://github.com/ultrasoundmoney/reth-block-validator", branch = "changes-to-enable-validation-api-extension" } | ||
serde = "1.0.188" | ||
serde-this-or-that = "0.4.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Reth based Builder Payload Validator Api | ||
Reth rpc extension to add an endpoint for validation of builder submissions as received by a relay. | ||
|
||
## Get Started | ||
Run extended reth full node with the added rpc endpoint with: | ||
`RUST_LOG=info cargo run -- node --full --metrics 127.0.0.1:9001 --http --enable-ext` | ||
|
||
## Test it | ||
While there are no automated tests yet you can execute a manual test using the provided testdata: | ||
`curl --location 'localhost:8545/' --header 'Content-Type: application/json' --data @test/data/rpc_payload.json` | ||
|
||
## Further Reading | ||
- [Guide to custom api development based on reth](https://www.libevm.com/2023/09/01/reth-custom-api/) | ||
- [Official example for adding rpc namespace](https://github.com/paradigmxyz/reth/blob/main/examples/additional-rpc-namespace-in-cli/src/main.rs) | ||
|
||
## Disclaimer | ||
This code is being provided as is. No guarantee, representation or warranty is being made, express or implied, as to the safety or correctness of the code. It has not been audited and as such there can be no assurance it will work as intended, and users may experience delays, failures, errors, omissions or loss of transmitted information. | ||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use reth::{ | ||
cli::{ | ||
config::RethRpcConfig, | ||
ext::{RethCliExt, RethNodeCommandConfig}, | ||
}, | ||
network::{NetworkInfo, Peers}, | ||
providers::{ | ||
AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, | ||
ChangeSetReader, EvmEnvProvider, StateProviderFactory, | ||
}, | ||
rpc::builder::{RethModuleRegistry, TransportRpcModules}, | ||
tasks::TaskSpawner, | ||
transaction_pool::TransactionPool, | ||
}; | ||
|
||
use crate::rpc::ValidationApiServer; | ||
use crate::ValidationApi; | ||
|
||
/// The type that tells the reth CLI what extensions to use | ||
pub struct ValidationCliExt; | ||
|
||
impl RethCliExt for ValidationCliExt { | ||
/// This tells the reth CLI to install the `validation` rpc namespace via `RethCliValidationApi` | ||
type Node = RethCliValidationApi; | ||
} | ||
|
||
/// Our custom cli args extension that adds one flag to reth default CLI. | ||
#[derive(Debug, Clone, Copy, Default, clap::Args)] | ||
pub struct RethCliValidationApi { | ||
/// CLI flag to enable the validation extension namespace | ||
#[clap(long)] | ||
pub enable_ext: bool, | ||
} | ||
|
||
impl RethNodeCommandConfig for RethCliValidationApi { | ||
// This is the entrypoint for the CLI to extend the RPC server with custom rpc namespaces. | ||
fn extend_rpc_modules<Conf, Provider, Pool, Network, Tasks, Events>( | ||
&mut self, | ||
_config: &Conf, | ||
registry: &mut RethModuleRegistry<Provider, Pool, Network, Tasks, Events>, | ||
modules: &mut TransportRpcModules, | ||
) -> eyre::Result<()> | ||
where | ||
Conf: RethRpcConfig, | ||
Provider: BlockReaderIdExt | ||
+ AccountReader | ||
+ StateProviderFactory | ||
+ EvmEnvProvider | ||
+ ChainSpecProvider | ||
+ ChangeSetReader | ||
+ Clone | ||
+ Unpin | ||
+ 'static, | ||
Pool: TransactionPool + Clone + 'static, | ||
Network: NetworkInfo + Peers + Clone + 'static, | ||
Tasks: TaskSpawner + Clone + 'static, | ||
Events: CanonStateSubscriptions + Clone + 'static, | ||
{ | ||
if !self.enable_ext { | ||
return Ok(()); | ||
} | ||
|
||
// here we get the configured pool type from the CLI. | ||
let provider = registry.provider().clone(); | ||
let ext = ValidationApi::new(provider); | ||
|
||
// now we merge our extension namespace into all configured transports | ||
modules.merge_configured(ext.into_rpc())?; | ||
|
||
println!("validation extension enabled"); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
//! Reth RPC extension to add endpoint for builder payload validation | ||
//! | ||
//! Run with | ||
//! | ||
//! ```not_rust | ||
//! RUST_LOG=info cargo run -- node --full --metrics 127.0.0.1:9001 --http --enable-ext | ||
//! ``` | ||
//! | ||
//! This installs an additional RPC method that can be queried using the provided sample rpc | ||
//! payload | ||
//! | ||
//! ```sh | ||
//! curl --location 'localhost:8545/' --header 'Content-Type: application/json' --data @test/data/rpc_payload.json | ||
//! ``` | ||
use clap::Parser; | ||
use reth::cli::Cli; | ||
use std::sync::Arc; | ||
|
||
mod cli_ext; | ||
use cli_ext::ValidationCliExt; | ||
|
||
mod rpc; | ||
use rpc::ValidationApiInner; | ||
|
||
fn main() { | ||
Cli::<ValidationCliExt>::parse().run().unwrap(); | ||
} | ||
|
||
/// The type that implements the `validation` rpc namespace trait | ||
pub struct ValidationApi<Provider> { | ||
inner: Arc<ValidationApiInner<Provider>>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
use async_trait::async_trait; | ||
use jsonrpsee::{core::RpcResult, proc_macros::rpc}; | ||
|
||
use reth::consensus_common::validation::full_validation; | ||
use reth::providers::{ | ||
AccountReader, BlockReaderIdExt, ChainSpecProvider, ChangeSetReader, HeaderProvider, | ||
StateProviderFactory, WithdrawalsProvider, | ||
}; | ||
use reth::rpc::result::ToRpcResultExt; | ||
use reth::rpc::types_compat::engine::payload::try_into_sealed_block; | ||
|
||
use std::sync::Arc; | ||
|
||
use crate::ValidationApi; | ||
|
||
mod types; | ||
use types::ExecutionPayloadValidation; | ||
|
||
/// trait interface for a custom rpc namespace: `validation` | ||
/// | ||
/// This defines an additional namespace where all methods are configured as trait functions. | ||
#[rpc(server, namespace = "validationExt")] | ||
#[async_trait] | ||
pub trait ValidationApi { | ||
/// Validates a block submitted to the relay | ||
#[method(name = "validateBuilderSubmissionV1")] | ||
async fn validate_builder_submission_v1( | ||
&self, | ||
execution_payload: ExecutionPayloadValidation, | ||
) -> RpcResult<()>; | ||
} | ||
|
||
impl<Provider> ValidationApi<Provider> { | ||
/// The provider that can interact with the chain. | ||
pub fn provider(&self) -> &Provider { | ||
&self.inner.provider | ||
} | ||
|
||
/// Create a new instance of the [ValidationApi] | ||
pub fn new(provider: Provider) -> Self { | ||
let inner = Arc::new(ValidationApiInner { provider }); | ||
Self { inner } | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl<Provider> ValidationApiServer for ValidationApi<Provider> | ||
where | ||
Provider: BlockReaderIdExt | ||
+ ChainSpecProvider | ||
+ ChangeSetReader | ||
+ StateProviderFactory | ||
+ HeaderProvider | ||
+ AccountReader | ||
+ WithdrawalsProvider | ||
+ 'static, | ||
{ | ||
/// Validates a block submitted to the relay | ||
async fn validate_builder_submission_v1( | ||
&self, | ||
execution_payload: ExecutionPayloadValidation, | ||
) -> RpcResult<()> { | ||
let block = try_into_sealed_block(execution_payload.into(), None).map_ok_or_rpc_err()?; | ||
let chain_spec = self.provider().chain_spec(); | ||
full_validation(&block, self.provider(), &chain_spec).map_ok_or_rpc_err() | ||
} | ||
} | ||
|
||
impl<Provider> std::fmt::Debug for ValidationApi<Provider> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_struct("ValidationApi").finish_non_exhaustive() | ||
} | ||
} | ||
|
||
impl<Provider> Clone for ValidationApi<Provider> { | ||
fn clone(&self) -> Self { | ||
Self { | ||
inner: Arc::clone(&self.inner), | ||
} | ||
} | ||
} | ||
|
||
pub struct ValidationApiInner<Provider> { | ||
/// The provider that can interact with the chain. | ||
provider: Provider, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use derivative::Derivative; | ||
use reth::primitives::{Address, Bloom, Bytes, H256, U256}; | ||
use reth::rpc::types::{ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, Withdrawal}; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_this_or_that::as_u64; | ||
|
||
/// Structure to deserialize execution payloads sent according to the builder api spec | ||
/// Numeric fields deserialized as decimals (unlike crate::eth::engine::ExecutionPayload) | ||
#[derive(Derivative)] | ||
#[derivative(Debug)] | ||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[allow(missing_docs)] | ||
pub struct ExecutionPayloadValidation { | ||
pub parent_hash: H256, | ||
pub fee_recipient: Address, | ||
pub state_root: H256, | ||
pub receipts_root: H256, | ||
pub logs_bloom: Bloom, | ||
pub prev_randao: H256, | ||
#[serde(deserialize_with = "as_u64")] | ||
pub block_number: u64, | ||
#[serde(deserialize_with = "as_u64")] | ||
pub gas_limit: u64, | ||
#[serde(deserialize_with = "as_u64")] | ||
pub gas_used: u64, | ||
#[serde(deserialize_with = "as_u64")] | ||
pub timestamp: u64, | ||
pub extra_data: Bytes, | ||
pub base_fee_per_gas: U256, | ||
pub block_hash: H256, | ||
#[derivative(Debug = "ignore")] | ||
pub transactions: Vec<Bytes>, | ||
pub withdrawals: Vec<WithdrawalValidation>, | ||
} | ||
|
||
/// Withdrawal object with numbers deserialized as decimals | ||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct WithdrawalValidation { | ||
/// Monotonically increasing identifier issued by consensus layer. | ||
#[serde(deserialize_with = "as_u64")] | ||
pub index: u64, | ||
/// Index of validator associated with withdrawal. | ||
#[serde(deserialize_with = "as_u64")] | ||
pub validator_index: u64, | ||
/// Target address for withdrawn ether. | ||
pub address: Address, | ||
/// Value of the withdrawal in gwei. | ||
#[serde(deserialize_with = "as_u64")] | ||
pub amount: u64, | ||
} | ||
|
||
impl From<ExecutionPayloadValidation> for ExecutionPayload { | ||
fn from(val: ExecutionPayloadValidation) -> Self { | ||
ExecutionPayload::V2(ExecutionPayloadV2 { | ||
payload_inner: ExecutionPayloadV1 { | ||
parent_hash: val.parent_hash, | ||
fee_recipient: val.fee_recipient, | ||
state_root: val.state_root, | ||
receipts_root: val.receipts_root, | ||
logs_bloom: val.logs_bloom, | ||
prev_randao: val.prev_randao, | ||
block_number: val.block_number.into(), | ||
gas_limit: val.gas_limit.into(), | ||
gas_used: val.gas_used.into(), | ||
timestamp: val.timestamp.into(), | ||
extra_data: val.extra_data, | ||
base_fee_per_gas: val.base_fee_per_gas, | ||
block_hash: val.block_hash, | ||
transactions: val.transactions, | ||
}, | ||
withdrawals: val.withdrawals.into_iter().map(|w| w.into()).collect(), | ||
}) | ||
} | ||
} | ||
|
||
impl From<WithdrawalValidation> for Withdrawal { | ||
fn from(val: WithdrawalValidation) -> Self { | ||
Withdrawal { | ||
index: val.index, | ||
validator_index: val.validator_index, | ||
address: val.address, | ||
amount: val.amount, | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need new types here? Seems like maintain headache, do we need to make the deser logic better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really just because of difference in the formatting of the current api that we are using (snake_case and decimal encoded numbers).
I agree this is a headache and we should probably just use the existing formatting (camelCase and hex numbers), in which case we can drop these additional types and just use the existing
ExecutionPayload
struct.@alextes What do you think ? (as is we will have to adjust the client code anyway because this api is differs from the existing one already)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do that I could probably also remove these re-exports from my pr against the reth repo. (if they are unwanted)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened an issue for this, and suggest doing this in a separate PR:
#2