diff --git a/config/example.toml b/config/example.toml index 2ad3d79a..04159743 100644 --- a/config/example.toml +++ b/config/example.toml @@ -68,13 +68,13 @@ response_cache_max_bytes = 10_000_000_000 [balanced_rpcs.llamanodes] display_name = "LlamaNodes" - block_data_limit = "Archive" + block_data_limit = "archive" http_url = "https://ethereum.llamarpc.com" ws_url = "wss://ethereum.llamarpc.com" [balanced_rpcs.ankr] display_name = "Ankr" - block_data_limit = "64" + block_data_limit = 64 http_url = "https://rpc.ankr.com/eth" soft_limit = 1_000 diff --git a/web3_proxy/src/config.rs b/web3_proxy/src/config.rs index 9e3e40da..f72d60c3 100644 --- a/web3_proxy/src/config.rs +++ b/web3_proxy/src/config.rs @@ -3,14 +3,14 @@ use crate::compute_units::default_usd_per_cu; use crate::rpcs::blockchain::{BlocksByHashCache, Web3ProxyBlock}; use crate::rpcs::one::Web3Rpc; use argh::FromArgs; -use derivative::Derivative; use ethers::prelude::{Address, TxHash}; use ethers::types::{U256, U64}; use hashbrown::HashMap; use migration::sea_orm::prelude::Decimal; use sentry::types::Dsn; -use serde::Deserialize; +use serde::{de, Deserialize, Deserializer}; use serde_inline_default::serde_inline_default; +use std::fmt; use std::sync::atomic::AtomicU64; use std::sync::Arc; use std::time::Duration; @@ -278,25 +278,59 @@ pub fn average_block_interval(chain_id: u64) -> Duration { } } -#[derive(Clone, Debug, Derivative, Deserialize, PartialEq, Eq)] -#[derivative(Default(bound = ""))] -#[serde(untagged)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub enum BlockDataLimit { /// archive nodes can return all data Archive, /// prune nodes don't have all the data /// some devs will argue about what "prune" means but we use it to mean that any of the data is gone. - Limit(u64), + /// TODO: this is too simple. erigon can prune the different types of data differently + Set(u64), /// Automatically detect the limit - #[derivative(Default)] + #[default] Unknown, } +impl<'de> Deserialize<'de> for BlockDataLimit { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct BlockDataLimitVisitor; + + impl<'de> de::Visitor<'de> for BlockDataLimitVisitor { + type Value = BlockDataLimit; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string 'archive', 'unknown' or an positive signed 64-bit integer. 0 means automatically detect") + } + + fn visit_str(self, value: &str) -> Result { + match value.to_ascii_lowercase().as_str() { + "archive" => Ok(BlockDataLimit::Archive), + "unknown" => Ok(BlockDataLimit::Unknown), + _ => Err(de::Error::custom(format!("Unexpected value {}", value))), + } + } + + fn visit_i64(self, v: i64) -> Result { + if v < 0 { + Err(de::Error::custom("Negative values are not allowed")) + } else { + Ok(BlockDataLimit::Set(v as u64)) + } + } + } + + deserializer.deserialize_any(BlockDataLimitVisitor) + } +} + impl From for AtomicU64 { fn from(value: BlockDataLimit) -> Self { match value { BlockDataLimit::Archive => AtomicU64::new(u64::MAX), - BlockDataLimit::Limit(limit) => AtomicU64::new(limit), + BlockDataLimit::Set(limit) => AtomicU64::new(limit), BlockDataLimit::Unknown => AtomicU64::new(0), } }