diff --git a/Cargo.lock b/Cargo.lock index 2346db1ce..7fb871df4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7931,6 +7931,8 @@ dependencies = [ "bytes", "fallible-iterator", "postgres-protocol", + "serde_core", + "serde_json", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 441445d40..ce15321af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,7 @@ teloxide = { version = "0.13", features = ["macros"] } thiserror = "1.0.49" tikv-jemallocator = { version = "0.6", features = ["profiling"] } tokio = { version = "1.43", features = ["full"] } -tokio-postgres = "0.7.10" +tokio-postgres = { version = "0.7.10", features = ["with-serde_json-1"]} tokio-tungstenite = "0.27.0" tonic = { version = "0.10", features = ["gzip"] } tonic-build = "0.10" diff --git a/crates/common/src/adjustments.rs b/crates/common/src/adjustments.rs index 4153dc0b8..da7b20c14 100644 --- a/crates/common/src/adjustments.rs +++ b/crates/common/src/adjustments.rs @@ -15,4 +15,5 @@ pub struct DataAdjustmentsEntry { pub adjusted_block_hash: B256, pub adjusted_value: U256, pub is_dry_run: bool, + pub metadata: serde_json::Value, } diff --git a/crates/common/src/config.rs b/crates/common/src/config.rs index b647d7a32..102d7dcf1 100644 --- a/crates/common/src/config.rs +++ b/crates/common/src/config.rs @@ -57,6 +57,8 @@ pub struct RelayConfig { pub cores: CoresConfig, #[serde(default = "default_bool::")] pub gossip_payload_on_header: bool, + #[serde(default = "default_u16::<4040>")] + pub api_port: u16, } impl RelayConfig { @@ -90,6 +92,7 @@ impl RelayConfig { reg_workers: vec![], }, gossip_payload_on_header: false, + api_port: 4040, } } } @@ -182,7 +185,7 @@ pub fn load_keypair() -> BlsKeypair { #[derive(Serialize, Deserialize, Clone, Default)] pub struct PostgresConfig { pub hostname: String, - #[serde(default = "default_port")] + #[serde(default = "default_u16::<5432>")] pub port: u16, pub db_name: String, pub user: String, @@ -204,8 +207,8 @@ pub struct BlockMergingConfig { pub is_dry_run: bool, } -fn default_port() -> u16 { - 5432 +pub const fn default_u16() -> u16 { + U } pub const fn default_bool() -> bool { diff --git a/crates/common/src/validator_preferences/mod.rs b/crates/common/src/validator_preferences/mod.rs index dbdf890ce..3d9cf805e 100644 --- a/crates/common/src/validator_preferences/mod.rs +++ b/crates/common/src/validator_preferences/mod.rs @@ -21,6 +21,9 @@ pub struct ValidatorPreferences { #[serde(default)] pub disable_inclusion_lists: bool, + + #[serde(default)] + pub disable_optimistic: bool, } impl Default for ValidatorPreferences { @@ -31,6 +34,7 @@ impl Default for ValidatorPreferences { header_delay: true, delay_ms: None, disable_inclusion_lists: false, + disable_optimistic: false, } } } @@ -72,6 +76,7 @@ pub struct BuilderValidatorPreferences { pub censoring: bool, pub filtering: Filtering, pub trusted_builders: Option>, + pub disable_optimistic: bool, } impl From for BuilderValidatorPreferences { @@ -80,6 +85,7 @@ impl From for BuilderValidatorPreferences { censoring: preferences.filtering.is_regional(), filtering: preferences.filtering, trusted_builders: preferences.trusted_builders.clone(), + disable_optimistic: preferences.disable_optimistic, } } } @@ -92,6 +98,7 @@ fn test_validator_preferences_serde() { header_delay: false, delay_ms: Some(1000), disable_inclusion_lists: true, + disable_optimistic: true, }; let json = serde_json::to_string(&preferences).unwrap(); diff --git a/crates/relay/src/api/proposer/register.rs b/crates/relay/src/api/proposer/register.rs index 4e1cedb5f..f8a506003 100644 --- a/crates/relay/src/api/proposer/register.rs +++ b/crates/relay/src/api/proposer/register.rs @@ -75,6 +75,7 @@ impl ProposerApi { header_delay: proposer_api.validator_preferences.header_delay, delay_ms: proposer_api.validator_preferences.delay_ms, disable_inclusion_lists: proposer_api.validator_preferences.disable_inclusion_lists, + disable_optimistic: proposer_api.validator_preferences.disable_optimistic, }; let preferences_header = headers.get("x-preferences"); @@ -107,6 +108,10 @@ impl ProposerApi { if let Some(header_delay) = preferences.header_delay { validator_preferences.header_delay = header_delay; } + + if let Some(disable_optimistic) = preferences.disable_optimistic { + validator_preferences.disable_optimistic = disable_optimistic; + } } let user_agent = proposer_api.api_provider.get_metadata(&headers); diff --git a/crates/relay/src/api/proposer/types.rs b/crates/relay/src/api/proposer/types.rs index 467ce8f26..7e3dd4a2c 100644 --- a/crates/relay/src/api/proposer/types.rs +++ b/crates/relay/src/api/proposer/types.rs @@ -18,4 +18,7 @@ pub struct PreferencesHeader { /// Allows validators to express a preference for whether a delay should be applied to get /// headers or not. pub header_delay: Option, + + /// Allows validators to opt out of optimistic bid submissions. + pub disable_optimistic: Option, } diff --git a/crates/relay/src/api/service.rs b/crates/relay/src/api/service.rs index 9d9465a8f..1e3f419bb 100644 --- a/crates/relay/src/api/service.rs +++ b/crates/relay/src/api/service.rs @@ -125,7 +125,8 @@ pub async fn start_api_service( terminating, ); - let listener = tokio::net::TcpListener::bind("0.0.0.0:4040").await.unwrap(); + let listener = + tokio::net::TcpListener::bind(format!("0.0.0.0:{}", config.api_port)).await.unwrap(); match axum::serve(listener, router.into_make_service_with_connect_info::()).await { Ok(_) => info!("Server exited successfully"), Err(e) => error!("Server exited with error: {e}"), diff --git a/crates/relay/src/auctioneer/context.rs b/crates/relay/src/auctioneer/context.rs index cb7163993..18096467a 100644 --- a/crates/relay/src/auctioneer/context.rs +++ b/crates/relay/src/auctioneer/context.rs @@ -229,7 +229,7 @@ impl Context { self.block_merger.on_new_slot(bid_slot.as_u64()); self.bid_adjustor.on_new_slot(bid_slot.as_u64()); - if self.payloads.len() > 0 { + if !self.payloads.is_empty() { // here we need to deallocate a lot of data, taking more than 1s on busy slots // this is not a big issue since it 's only at the beginning of the slot, but it blocks // the full event loop, which is not ideal. An alternative would be to use a diff --git a/crates/relay/src/auctioneer/submit_block.rs b/crates/relay/src/auctioneer/submit_block.rs index 17084db4e..bb52ee2dc 100644 --- a/crates/relay/src/auctioneer/submit_block.rs +++ b/crates/relay/src/auctioneer/submit_block.rs @@ -12,7 +12,7 @@ use helix_types::{ SubmissionVersion, }; use tokio::sync::oneshot; -use tracing::{error, trace, warn}; +use tracing::{error, trace}; use crate::{ api::builder::error::BuilderApiError, @@ -73,8 +73,6 @@ impl Context { self.block_merger.update_base_block(base_block); } self.request_merged_block(); - } else { - warn!("Block merging is disabled or no merging data provided"); } } @@ -287,6 +285,10 @@ impl Context { builder_info: &BuilderInfo, slot_data: &SlotData, ) -> bool { + if slot_data.registration_data.entry.preferences.disable_optimistic { + return false; + } + if builder_info.is_optimistic && submission.message().value <= builder_info.collateral { if slot_data.registration_data.entry.preferences.filtering.is_regional() && !builder_info.can_process_regional_slot_optimistically() diff --git a/crates/relay/src/database/postgres/migrations/V42__disable_optimistic.sql b/crates/relay/src/database/postgres/migrations/V42__disable_optimistic.sql new file mode 100644 index 000000000..ff82a4575 --- /dev/null +++ b/crates/relay/src/database/postgres/migrations/V42__disable_optimistic.sql @@ -0,0 +1,8 @@ +ALTER TABLE validator_preferences +ADD COLUMN IF NOT EXISTS "disable_optimistic" boolean NOT NULL DEFAULT false; + +ALTER TABLE delivered_payload_preferences +ADD COLUMN IF NOT EXISTS "disable_optimistic" boolean NOT NULL DEFAULT false; + +ALTER TABLE slot_preferences +ADD COLUMN IF NOT EXISTS "disable_optimistic" boolean NOT NULL DEFAULT false; diff --git a/crates/relay/src/database/postgres/migrations/V43__adjustments_metadata.sql b/crates/relay/src/database/postgres/migrations/V43__adjustments_metadata.sql new file mode 100644 index 000000000..9dcdb3cb7 --- /dev/null +++ b/crates/relay/src/database/postgres/migrations/V43__adjustments_metadata.sql @@ -0,0 +1,2 @@ +ALTER TABLE bid_adjustments + ADD COLUMN IF NOT EXISTS metadata JSONB; diff --git a/crates/relay/src/database/postgres/postgres_db_row_parsing.rs b/crates/relay/src/database/postgres/postgres_db_row_parsing.rs index 0721fec87..88f6af6e3 100644 --- a/crates/relay/src/database/postgres/postgres_db_row_parsing.rs +++ b/crates/relay/src/database/postgres/postgres_db_row_parsing.rs @@ -116,6 +116,7 @@ impl FromRow for BuilderGetValidatorsResponseEntry { .get::<&str, Option>("delay_ms") .and_then(|v| parse_i64_to_u64(v).ok()), disable_inclusion_lists: row.get::<&str, bool>("disable_inclusion_lists"), + disable_optimistic: row.get::<&str, bool>("disable_optimistic"), }, }, }) @@ -135,6 +136,7 @@ impl FromRow for ValidatorPreferences { .get::<&str, Option>("delay_ms") .and_then(|v| parse_i64_to_u64(v).ok()), disable_inclusion_lists: row.get::<&str, bool>("disable_inclusion_lists"), + disable_optimistic: row.get::<&str, bool>("disable_optimistic"), }) } } @@ -212,6 +214,7 @@ impl FromRow for SignedValidatorRegistrationEntry { .get::<&str, Option>("delay_ms") .and_then(|v| parse_i64_to_u64(v).ok()), disable_inclusion_lists: row.get::<&str, bool>("disable_inclusion_lists"), + disable_optimistic: row.get::<&str, bool>("disable_optimistic"), }, }, inserted_at: parse_timestamptz_to_u64( diff --git a/crates/relay/src/database/postgres/postgres_db_service.rs b/crates/relay/src/database/postgres/postgres_db_service.rs index b7cc4fa26..c7fc782f7 100644 --- a/crates/relay/src/database/postgres/postgres_db_service.rs +++ b/crates/relay/src/database/postgres/postgres_db_service.rs @@ -79,6 +79,7 @@ struct PreferenceParams { trusted_builders: Option>, header_delay: bool, disable_inclusion_lists: bool, + disable_optimistic: bool, } struct TrustedProposerParams { @@ -370,6 +371,7 @@ impl PostgresDatabaseService { .registration_info .preferences .disable_inclusion_lists, + disable_optimistic: entry.registration_info.preferences.disable_optimistic, }); if name.is_some() { @@ -426,15 +428,16 @@ impl PostgresDatabaseService { &tuple.trusted_builders, &tuple.header_delay, &tuple.disable_inclusion_lists, + &tuple.disable_optimistic, ] }) .collect(); // Construct the SQL statement with multiple VALUES clauses let mut sql = String::from( - "INSERT INTO validator_preferences (public_key, filtering, trusted_builders, header_delay, disable_inclusion_lists) VALUES ", + "INSERT INTO validator_preferences (public_key, filtering, trusted_builders, header_delay, disable_inclusion_lists, disable_optimistic) VALUES ", ); - let num_params_per_row = 5; + let num_params_per_row = 6; let values_clauses: Vec = (0..params.len() / num_params_per_row) .map(|row| { let placeholders: Vec = (1..=num_params_per_row) @@ -447,11 +450,12 @@ impl PostgresDatabaseService { // Join the values clauses and append them to the SQL statement sql.push_str(&values_clauses.join(", ")); sql.push_str( - " ON CONFLICT (public_key) DO UPDATE SET - filtering = excluded.filtering, - trusted_builders = excluded.trusted_builders, + " ON CONFLICT (public_key) DO UPDATE SET + filtering = excluded.filtering, + trusted_builders = excluded.trusted_builders, header_delay = excluded.header_delay, - disable_inclusion_lists = excluded.disable_inclusion_lists + disable_inclusion_lists = excluded.disable_inclusion_lists, + disable_optimistic = excluded.disable_optimistic WHERE validator_preferences.manual_override = FALSE", ); @@ -723,6 +727,7 @@ impl PostgresDatabaseService { validator_preferences.trusted_builders, validator_preferences.header_delay, validator_preferences.disable_inclusion_lists, + validator_preferences.disable_optimistic, validator_registrations.inserted_at, validator_registrations.user_agent, validator_preferences.delay_ms @@ -971,15 +976,17 @@ impl PostgresDatabaseService { header_delay, delay_ms, disable_inclusion_lists, + disable_optimistic, manual_override - ) VALUES ($1, $2, $3, $4, $5, $6, TRUE) - ON CONFLICT (public_key) + ) VALUES ($1, $2, $3, $4, $5, $6, $7, TRUE) + ON CONFLICT (public_key) DO UPDATE SET filtering = EXCLUDED.filtering, trusted_builders = EXCLUDED.trusted_builders, header_delay = EXCLUDED.header_delay, delay_ms = EXCLUDED.delay_ms, disable_inclusion_lists = EXCLUDED.disable_inclusion_lists, + disable_optimistic = EXCLUDED.disable_optimistic, manual_override = TRUE ", &[ @@ -989,6 +996,7 @@ impl PostgresDatabaseService { &preferences.header_delay, &preferences.delay_ms.map(|v| v as i64), &preferences.disable_inclusion_lists, + &preferences.disable_optimistic, ], ) .await?; @@ -2250,10 +2258,11 @@ impl PostgresDatabaseService { submitted_value, adjusted_block_hash, adjusted_value, - is_dry_run + is_dry_run, + metadata ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) ", &[ &(entry.slot.as_u64() as i64), @@ -2266,6 +2275,7 @@ impl PostgresDatabaseService { &entry.adjusted_block_hash.as_slice(), &PostgresNumeric::from(entry.adjusted_value), &entry.is_dry_run, + &entry.metadata, ], ) .await?; diff --git a/crates/simulator/src/block_merging/mod.rs b/crates/simulator/src/block_merging/mod.rs index 04a883c4e..b173b6929 100644 --- a/crates/simulator/src/block_merging/mod.rs +++ b/crates/simulator/src/block_merging/mod.rs @@ -25,7 +25,8 @@ use reth_ethereum::{ primitives::{ Evm, EvmEnvFor, EvmError, block::{BlockExecutionError, BlockExecutor}, - execute::BlockBuilder as RethBlockBuilder, tx, + execute::BlockBuilder as RethBlockBuilder, + tx, }, revm::{cached::CachedReads, database::StateProviderDatabase}, }, @@ -38,7 +39,10 @@ use reth_node_builder::{ }; use reth_primitives::{GotExpected, Recovered}; use revm::{ - DatabaseCommit, DatabaseRef, database::{CacheDB, State}, interpreter::gas, state::AccountInfo + DatabaseCommit, DatabaseRef, + database::{CacheDB, State}, + interpreter::gas, + state::AccountInfo, }; use tracing::{debug, info, warn}; @@ -388,7 +392,8 @@ impl BlockMergingApi { return Err(BlockMergingApiError::ZeroRevenueForWinningBuilder); } - let remaining_gas = MAX_TX_GAS_LIMIT_OSAKA.min(header.gas_limit.saturating_sub(builder.gas_used)); + let remaining_gas = + MAX_TX_GAS_LIMIT_OSAKA.min(header.gas_limit.saturating_sub(builder.gas_used)); self.append_payment_tx( &mut builder, @@ -1012,7 +1017,6 @@ where Ok(result) => { let tx_gas_used = result.result.gas_used(); if result.result.is_success() || can_revert { - if gas_used + tx_gas_used > available_gas { if !can_be_dropped { return Err(SimulationError::OutOfBlockGas);