Merge branch 'main' into devel
This commit is contained in:
commit
4889c3e1ce
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3148,7 +3148,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "migration"
|
name = "migration"
|
||||||
version = "0.17.0"
|
version = "0.19.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sea-orm-migration",
|
"sea-orm-migration",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "migration"
|
name = "migration"
|
||||||
version = "0.17.0"
|
version = "0.19.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
@ -209,6 +209,8 @@ impl DatabaseReplica {
|
|||||||
pub struct Web3ProxyApp {
|
pub struct Web3ProxyApp {
|
||||||
/// Send requests to the best server available
|
/// Send requests to the best server available
|
||||||
pub balanced_rpcs: Arc<Web3Rpcs>,
|
pub balanced_rpcs: Arc<Web3Rpcs>,
|
||||||
|
/// Send 4337 Abstraction Bundler requests to one of these servers
|
||||||
|
pub bundler_4337_rpcs: Option<Arc<Web3Rpcs>>,
|
||||||
pub http_client: Option<reqwest::Client>,
|
pub http_client: Option<reqwest::Client>,
|
||||||
/// application config
|
/// application config
|
||||||
/// TODO: this will need a large refactor to handle reloads while running. maybe use a watch::Receiver?
|
/// TODO: this will need a large refactor to handle reloads while running. maybe use a watch::Receiver?
|
||||||
@ -764,6 +766,34 @@ impl Web3ProxyApp {
|
|||||||
Some(private_rpcs)
|
Some(private_rpcs)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// prepare a Web3Rpcs to hold all our 4337 Abstraction Bundler connections
|
||||||
|
// only some chains have this, so this is optional
|
||||||
|
let bundler_4337_rpcs = if top_config.bundler_4337_rpcs.is_none() {
|
||||||
|
warn!("No bundler_4337_rpcs configured");
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// TODO: do something with the spawn handle
|
||||||
|
let (bundler_4337_rpcs, bundler_4337_rpcs_handle, _) = Web3Rpcs::spawn(
|
||||||
|
top_config.app.chain_id,
|
||||||
|
db_conn.clone(),
|
||||||
|
http_client.clone(),
|
||||||
|
// bundler_4337_rpcs don't get subscriptions, so no need for max_block_age or max_block_lag
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
pending_transactions.clone(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("spawning bundler_4337_rpcs")?;
|
||||||
|
|
||||||
|
app_handles.push(bundler_4337_rpcs_handle);
|
||||||
|
|
||||||
|
Some(bundler_4337_rpcs)
|
||||||
|
};
|
||||||
|
|
||||||
let hostname = hostname::get()
|
let hostname = hostname::get()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|x| x.to_str().map(|x| x.to_string()));
|
.and_then(|x| x.to_str().map(|x| x.to_string()));
|
||||||
@ -771,6 +801,7 @@ impl Web3ProxyApp {
|
|||||||
let app = Self {
|
let app = Self {
|
||||||
config: top_config.app.clone(),
|
config: top_config.app.clone(),
|
||||||
balanced_rpcs,
|
balanced_rpcs,
|
||||||
|
bundler_4337_rpcs,
|
||||||
http_client,
|
http_client,
|
||||||
kafka_producer,
|
kafka_producer,
|
||||||
private_rpcs,
|
private_rpcs,
|
||||||
@ -850,19 +881,33 @@ impl Web3ProxyApp {
|
|||||||
// connect to the backends
|
// connect to the backends
|
||||||
self.balanced_rpcs
|
self.balanced_rpcs
|
||||||
.apply_server_configs(self, new_top_config.balanced_rpcs)
|
.apply_server_configs(self, new_top_config.balanced_rpcs)
|
||||||
.await?;
|
.await
|
||||||
|
.context("updating balanced rpcs")?;
|
||||||
|
|
||||||
if let Some(private_rpc_configs) = new_top_config.private_rpcs {
|
if let Some(private_rpc_configs) = new_top_config.private_rpcs {
|
||||||
if let Some(private_rpcs) = self.private_rpcs.as_ref() {
|
if let Some(private_rpcs) = self.private_rpcs.as_ref() {
|
||||||
private_rpcs
|
private_rpcs
|
||||||
.apply_server_configs(self, private_rpc_configs)
|
.apply_server_configs(self, private_rpc_configs)
|
||||||
.await?;
|
.await
|
||||||
|
.context("updating private_rpcs")?;
|
||||||
} else {
|
} else {
|
||||||
// TODO: maybe we should have private_rpcs just be empty instead of being None
|
// TODO: maybe we should have private_rpcs just be empty instead of being None
|
||||||
todo!("handle toggling private_rpcs")
|
todo!("handle toggling private_rpcs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(bundler_4337_rpc_configs) = new_top_config.bundler_4337_rpcs {
|
||||||
|
if let Some(bundler_4337_rpcs) = self.bundler_4337_rpcs.as_ref() {
|
||||||
|
bundler_4337_rpcs
|
||||||
|
.apply_server_configs(self, bundler_4337_rpc_configs)
|
||||||
|
.await
|
||||||
|
.context("updating bundler_4337_rpcs")?;
|
||||||
|
} else {
|
||||||
|
// TODO: maybe we should have bundler_4337_rpcs just be empty instead of being None
|
||||||
|
todo!("handle toggling bundler_4337_rpcs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1158,6 +1203,7 @@ impl Web3ProxyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: more robust stats and kafka logic! if we use the try operator, they aren't saved!
|
||||||
async fn proxy_cached_request(
|
async fn proxy_cached_request(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
authorization: &Arc<Authorization>,
|
authorization: &Arc<Authorization>,
|
||||||
@ -1335,6 +1381,59 @@ impl Web3ProxyApp {
|
|||||||
vec![],
|
vec![],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
method @ ("debug_bundler_sendBundleNow"
|
||||||
|
| "debug_bundler_clearState"
|
||||||
|
| "debug_bundler_dumpMempool") => {
|
||||||
|
return Ok((
|
||||||
|
JsonRpcForwardedResponse::from_string(
|
||||||
|
// TODO: we should probably have some escaping on this. but maybe serde will protect us enough
|
||||||
|
format!("method unsupported: {}", method),
|
||||||
|
None,
|
||||||
|
Some(request_id),
|
||||||
|
),
|
||||||
|
vec![],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
method @ ("eth_sendUserOperation"
|
||||||
|
| "eth_estimateUserOperationGas"
|
||||||
|
| "eth_getUserOperationByHash"
|
||||||
|
| "eth_getUserOperationReceipt"
|
||||||
|
| "eth_supportedEntryPoints") => match self.bundler_4337_rpcs.as_ref() {
|
||||||
|
Some(bundler_4337_rpcs) => {
|
||||||
|
let response = bundler_4337_rpcs
|
||||||
|
.try_proxy_connection(
|
||||||
|
authorization,
|
||||||
|
request,
|
||||||
|
Some(&request_metadata),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// TODO: DRY
|
||||||
|
let rpcs = request_metadata.backend_requests.lock().clone();
|
||||||
|
|
||||||
|
if let Some(stat_sender) = self.stat_sender.as_ref() {
|
||||||
|
let response_stat = RpcQueryStats::new(
|
||||||
|
Some(method.to_string()),
|
||||||
|
authorization.clone(),
|
||||||
|
request_metadata,
|
||||||
|
response.num_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
stat_sender
|
||||||
|
.send_async(response_stat.into())
|
||||||
|
.await
|
||||||
|
.map_err(Web3ProxyError::SendAppStatError)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok((response, rpcs));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// TODO: stats!
|
||||||
|
return Err(anyhow::anyhow!("no bundler_4337_rpcs available").into());
|
||||||
|
}
|
||||||
|
},
|
||||||
// some commands can use local data or caches
|
// some commands can use local data or caches
|
||||||
"eth_accounts" => {
|
"eth_accounts" => {
|
||||||
// no stats on this. its cheap
|
// no stats on this. its cheap
|
||||||
@ -1379,6 +1478,8 @@ impl Web3ProxyApp {
|
|||||||
// i think this is always an error response
|
// i think this is always an error response
|
||||||
let rpcs = request_metadata.backend_requests.lock().clone();
|
let rpcs = request_metadata.backend_requests.lock().clone();
|
||||||
|
|
||||||
|
// TODO! save stats
|
||||||
|
|
||||||
return Ok((response, rpcs));
|
return Ok((response, rpcs));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1574,7 +1675,6 @@ impl Web3ProxyApp {
|
|||||||
serde_json::Value::String(APP_USER_AGENT.to_string())
|
serde_json::Value::String(APP_USER_AGENT.to_string())
|
||||||
}
|
}
|
||||||
"web3_sha3" => {
|
"web3_sha3" => {
|
||||||
// emit stats
|
|
||||||
// returns Keccak-256 (not the standardized SHA3-256) of the given data.
|
// returns Keccak-256 (not the standardized SHA3-256) of the given data.
|
||||||
match &request.params {
|
match &request.params {
|
||||||
Some(serde_json::Value::Array(params)) => {
|
Some(serde_json::Value::Array(params)) => {
|
||||||
@ -1641,8 +1741,6 @@ impl Web3ProxyApp {
|
|||||||
return Err(Web3ProxyError::AccessDenied);
|
return Err(Web3ProxyError::AccessDenied);
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit stats
|
|
||||||
|
|
||||||
// TODO: if no servers synced, wait for them to be synced? probably better to error and let haproxy retry another server
|
// TODO: if no servers synced, wait for them to be synced? probably better to error and let haproxy retry another server
|
||||||
let head_block_num = head_block_num
|
let head_block_num = head_block_num
|
||||||
.or(self.balanced_rpcs.head_block_num())
|
.or(self.balanced_rpcs.head_block_num())
|
||||||
|
@ -401,6 +401,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
private_rpcs: None,
|
private_rpcs: None,
|
||||||
|
bundler_4337_rpcs: None,
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@ impl RpcAccountingSubCommand {
|
|||||||
#[derive(Serialize, FromQueryResult)]
|
#[derive(Serialize, FromQueryResult)]
|
||||||
struct SelectResult {
|
struct SelectResult {
|
||||||
total_frontend_requests: Decimal,
|
total_frontend_requests: Decimal,
|
||||||
// pub total_backend_retries: Decimal,
|
total_backend_retries: Decimal,
|
||||||
// pub total_cache_misses: Decimal,
|
// total_cache_misses: Decimal,
|
||||||
total_cache_hits: Decimal,
|
total_cache_hits: Decimal,
|
||||||
total_response_bytes: Decimal,
|
total_response_bytes: Decimal,
|
||||||
total_error_responses: Decimal,
|
total_error_responses: Decimal,
|
||||||
// pub total_response_millis: Decimal,
|
total_response_millis: Decimal,
|
||||||
first_period_datetime: DateTimeUtc,
|
first_period_datetime: DateTimeUtc,
|
||||||
last_period_datetime: DateTimeUtc,
|
last_period_datetime: DateTimeUtc,
|
||||||
}
|
}
|
||||||
@ -58,10 +58,10 @@ impl RpcAccountingSubCommand {
|
|||||||
rpc_accounting::Column::FrontendRequests.sum(),
|
rpc_accounting::Column::FrontendRequests.sum(),
|
||||||
"total_frontend_requests",
|
"total_frontend_requests",
|
||||||
)
|
)
|
||||||
// .column_as(
|
.column_as(
|
||||||
// rpc_accounting::Column::BackendRequests.sum(),
|
rpc_accounting::Column::BackendRequests.sum(),
|
||||||
// "total_backend_retries",
|
"total_backend_retries",
|
||||||
// )
|
)
|
||||||
// .column_as(
|
// .column_as(
|
||||||
// rpc_accounting::Column::CacheMisses.sum(),
|
// rpc_accounting::Column::CacheMisses.sum(),
|
||||||
// "total_cache_misses",
|
// "total_cache_misses",
|
||||||
@ -76,10 +76,10 @@ impl RpcAccountingSubCommand {
|
|||||||
rpc_accounting::Column::ErrorResponse.sum(),
|
rpc_accounting::Column::ErrorResponse.sum(),
|
||||||
"total_error_responses",
|
"total_error_responses",
|
||||||
)
|
)
|
||||||
// .column_as(
|
.column_as(
|
||||||
// rpc_accounting::Column::SumResponseMillis.sum(),
|
rpc_accounting::Column::SumResponseMillis.sum(),
|
||||||
// "total_response_millis",
|
"total_response_millis",
|
||||||
// )
|
)
|
||||||
.column_as(
|
.column_as(
|
||||||
rpc_accounting::Column::PeriodDatetime.min(),
|
rpc_accounting::Column::PeriodDatetime.min(),
|
||||||
"first_period_datetime",
|
"first_period_datetime",
|
||||||
@ -131,25 +131,42 @@ impl RpcAccountingSubCommand {
|
|||||||
|
|
||||||
q = q.filter(condition);
|
q = q.filter(condition);
|
||||||
|
|
||||||
// TODO: make this work without into_json. i think we need to make a struct
|
let stats = q
|
||||||
let query_response = q
|
|
||||||
.into_model::<SelectResult>()
|
.into_model::<SelectResult>()
|
||||||
.one(db_conn)
|
.one(db_conn)
|
||||||
.await?
|
.await?
|
||||||
.context("no query result")?;
|
.context("no query result")?;
|
||||||
|
|
||||||
info!(
|
if let Some(chain_id) = self.chain_id {
|
||||||
"query_response for chain {:?}: {:#}",
|
info!("stats for chain {}", chain_id);
|
||||||
self.chain_id,
|
} else {
|
||||||
json!(query_response)
|
info!("stats for all chains");
|
||||||
);
|
}
|
||||||
|
|
||||||
// let query_seconds: Decimal = query_response
|
info!("stats: {:#}", json!(&stats));
|
||||||
// .last_period_datetime
|
|
||||||
// .signed_duration_since(query_response.first_period_datetime)
|
let query_seconds: Decimal = stats
|
||||||
// .num_seconds()
|
.last_period_datetime
|
||||||
// .into();
|
.signed_duration_since(stats.first_period_datetime)
|
||||||
// info!("query seconds: {}", query_seconds);
|
.num_seconds()
|
||||||
|
.into();
|
||||||
|
dbg!(query_seconds);
|
||||||
|
|
||||||
|
let avg_request_per_second = (stats.total_frontend_requests / query_seconds).round_dp(2);
|
||||||
|
dbg!(avg_request_per_second);
|
||||||
|
|
||||||
|
let cache_hit_rate = (stats.total_cache_hits / stats.total_frontend_requests
|
||||||
|
* Decimal::from(100))
|
||||||
|
.round_dp(2);
|
||||||
|
dbg!(cache_hit_rate);
|
||||||
|
|
||||||
|
let avg_response_millis =
|
||||||
|
(stats.total_response_millis / stats.total_frontend_requests).round_dp(3);
|
||||||
|
dbg!(avg_response_millis);
|
||||||
|
|
||||||
|
let avg_response_bytes =
|
||||||
|
(stats.total_response_bytes / stats.total_frontend_requests).round();
|
||||||
|
dbg!(avg_response_bytes);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,8 @@ pub struct CliConfig {
|
|||||||
pub struct TopConfig {
|
pub struct TopConfig {
|
||||||
pub app: AppConfig,
|
pub app: AppConfig,
|
||||||
pub balanced_rpcs: HashMap<String, Web3RpcConfig>,
|
pub balanced_rpcs: HashMap<String, Web3RpcConfig>,
|
||||||
// TODO: instead of an option, give it a default
|
|
||||||
pub private_rpcs: Option<HashMap<String, Web3RpcConfig>>,
|
pub private_rpcs: Option<HashMap<String, Web3RpcConfig>>,
|
||||||
|
pub bundler_4337_rpcs: Option<HashMap<String, Web3RpcConfig>>,
|
||||||
/// unknown config options get put here
|
/// unknown config options get put here
|
||||||
#[serde(flatten, default = "HashMap::default")]
|
#[serde(flatten, default = "HashMap::default")]
|
||||||
pub extra: HashMap<String, serde_json::Value>,
|
pub extra: HashMap<String, serde_json::Value>,
|
||||||
|
@ -164,6 +164,7 @@ pub async fn serve(
|
|||||||
//
|
//
|
||||||
.route("/health", get(status::health))
|
.route("/health", get(status::health))
|
||||||
.route("/status", get(status::status))
|
.route("/status", get(status::status))
|
||||||
|
.route("/status/backups_needed", get(status::backups_needed))
|
||||||
//
|
//
|
||||||
// User stuff
|
// User stuff
|
||||||
//
|
//
|
||||||
|
@ -27,6 +27,30 @@ pub async fn health(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Easy alerting if backup servers are in use.
|
||||||
|
pub async fn backups_needed(Extension(app): Extension<Arc<Web3ProxyApp>>) -> impl IntoResponse {
|
||||||
|
let code = {
|
||||||
|
let consensus_rpcs = app.balanced_rpcs.watch_consensus_rpcs_sender.borrow();
|
||||||
|
|
||||||
|
if let Some(consensus_rpcs) = consensus_rpcs.as_ref() {
|
||||||
|
if consensus_rpcs.backups_needed {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
} else {
|
||||||
|
StatusCode::OK
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if no consensus, we still "need backups". we just don't have any. which is worse
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches!(code, StatusCode::OK) {
|
||||||
|
(code, "no backups needed. :)")
|
||||||
|
} else {
|
||||||
|
(code, "backups needed! :(")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Very basic status page.
|
/// Very basic status page.
|
||||||
///
|
///
|
||||||
/// TODO: replace this with proper stats and monitoring
|
/// TODO: replace this with proper stats and monitoring
|
||||||
|
@ -18,12 +18,12 @@ use tokio::time::Instant;
|
|||||||
/// Serialize is so we can print it on our debug endpoint
|
/// Serialize is so we can print it on our debug endpoint
|
||||||
#[derive(Clone, Serialize)]
|
#[derive(Clone, Serialize)]
|
||||||
pub struct ConsensusWeb3Rpcs {
|
pub struct ConsensusWeb3Rpcs {
|
||||||
pub(super) tier: u64,
|
pub(crate) tier: u64,
|
||||||
pub(super) head_block: Web3ProxyBlock,
|
pub(crate) head_block: Web3ProxyBlock,
|
||||||
pub(super) best_rpcs: Vec<Arc<Web3Rpc>>,
|
pub(crate) best_rpcs: Vec<Arc<Web3Rpc>>,
|
||||||
// TODO: functions like "compare_backup_vote()"
|
// TODO: functions like "compare_backup_vote()"
|
||||||
// pub(super) backups_voted: Option<Web3ProxyBlock>,
|
// pub(super) backups_voted: Option<Web3ProxyBlock>,
|
||||||
pub(super) backups_needed: bool,
|
pub(crate) backups_needed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConsensusWeb3Rpcs {
|
impl ConsensusWeb3Rpcs {
|
||||||
|
@ -53,7 +53,7 @@ pub struct Web3Rpcs {
|
|||||||
/// TODO: document that this is a watch sender and not a broadcast! if things get busy, blocks might get missed
|
/// TODO: document that this is a watch sender and not a broadcast! if things get busy, blocks might get missed
|
||||||
/// TODO: why is watch_consensus_head_sender in an Option, but this one isn't?
|
/// TODO: why is watch_consensus_head_sender in an Option, but this one isn't?
|
||||||
/// Geth's subscriptions have the same potential for skipping blocks.
|
/// Geth's subscriptions have the same potential for skipping blocks.
|
||||||
pub(super) watch_consensus_rpcs_sender: watch::Sender<Option<Arc<ConsensusWeb3Rpcs>>>,
|
pub(crate) watch_consensus_rpcs_sender: watch::Sender<Option<Arc<ConsensusWeb3Rpcs>>>,
|
||||||
/// this head receiver makes it easy to wait until there is a new block
|
/// this head receiver makes it easy to wait until there is a new block
|
||||||
pub(super) watch_consensus_head_sender: Option<watch::Sender<Option<Web3ProxyBlock>>>,
|
pub(super) watch_consensus_head_sender: Option<watch::Sender<Option<Web3ProxyBlock>>>,
|
||||||
pub(super) pending_transaction_cache:
|
pub(super) pending_transaction_cache:
|
||||||
@ -102,6 +102,8 @@ impl Web3Rpcs {
|
|||||||
let expected_block_time_ms = match chain_id {
|
let expected_block_time_ms = match chain_id {
|
||||||
// ethereum
|
// ethereum
|
||||||
1 => 12_000,
|
1 => 12_000,
|
||||||
|
// ethereum-goerli
|
||||||
|
5 => 12_000,
|
||||||
// polygon
|
// polygon
|
||||||
137 => 2_000,
|
137 => 2_000,
|
||||||
// fantom
|
// fantom
|
||||||
@ -110,7 +112,10 @@ impl Web3Rpcs {
|
|||||||
42161 => 500,
|
42161 => 500,
|
||||||
// anything else
|
// anything else
|
||||||
_ => {
|
_ => {
|
||||||
warn!("unexpected chain_id. polling every {} seconds", 10);
|
warn!(
|
||||||
|
"unexpected chain_id ({}). polling every {} seconds",
|
||||||
|
chain_id, 10
|
||||||
|
);
|
||||||
10_000
|
10_000
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -214,11 +219,14 @@ impl Web3Rpcs {
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
// safety checks
|
// safety checks
|
||||||
if rpc_configs.len() < app.config.min_synced_rpcs {
|
if rpc_configs.len() < app.config.min_synced_rpcs {
|
||||||
return Err(anyhow::anyhow!(
|
// TODO: don't count disabled servers!
|
||||||
|
// TODO: include if this is balanced, private, or 4337
|
||||||
|
warn!(
|
||||||
"Only {}/{} rpcs! Add more rpcs or reduce min_synced_rpcs.",
|
"Only {}/{} rpcs! Add more rpcs or reduce min_synced_rpcs.",
|
||||||
rpc_configs.len(),
|
rpc_configs.len(),
|
||||||
app.config.min_synced_rpcs
|
app.config.min_synced_rpcs
|
||||||
));
|
);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// safety check on sum soft limit
|
// safety check on sum soft limit
|
||||||
@ -502,6 +510,24 @@ impl Web3Rpcs {
|
|||||||
max_block_needed: Option<&U64>,
|
max_block_needed: Option<&U64>,
|
||||||
) -> Web3ProxyResult<OpenRequestResult> {
|
) -> Web3ProxyResult<OpenRequestResult> {
|
||||||
let usable_rpcs_by_tier_and_head_number: BTreeMap<(u64, Option<U64>), Vec<Arc<Web3Rpc>>> = {
|
let usable_rpcs_by_tier_and_head_number: BTreeMap<(u64, Option<U64>), Vec<Arc<Web3Rpc>>> = {
|
||||||
|
if self.watch_consensus_head_sender.is_none() {
|
||||||
|
// pick any server
|
||||||
|
let mut m = BTreeMap::new();
|
||||||
|
|
||||||
|
let key = (0, None);
|
||||||
|
|
||||||
|
for x in self.by_name.read().values() {
|
||||||
|
if skip.contains(x) {
|
||||||
|
trace!("skipping: {}", x);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
trace!("not skipped!");
|
||||||
|
|
||||||
|
m.entry(key).or_insert_with(Vec::new).push(x.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
m
|
||||||
|
} else {
|
||||||
let synced_connections = self.watch_consensus_rpcs_sender.borrow().clone();
|
let synced_connections = self.watch_consensus_rpcs_sender.borrow().clone();
|
||||||
|
|
||||||
if synced_connections.is_none() {
|
if synced_connections.is_none() {
|
||||||
@ -551,7 +577,8 @@ impl Web3Rpcs {
|
|||||||
|
|
||||||
let min_block_age =
|
let min_block_age =
|
||||||
self.max_block_age.map(|x| head_block_age.saturating_sub(x));
|
self.max_block_age.map(|x| head_block_age.saturating_sub(x));
|
||||||
let min_sync_num = self.max_block_lag.map(|x| head_block_num.saturating_sub(x));
|
let min_sync_num =
|
||||||
|
self.max_block_lag.map(|x| head_block_num.saturating_sub(x));
|
||||||
|
|
||||||
// TODO: cache this somehow?
|
// TODO: cache this somehow?
|
||||||
// TODO: maybe have a helper on synced_connections? that way sum_soft_limits/min_synced_rpcs will be DRY
|
// TODO: maybe have a helper on synced_connections? that way sum_soft_limits/min_synced_rpcs will be DRY
|
||||||
@ -637,6 +664,7 @@ impl Web3Rpcs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m
|
m
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
|
@ -187,7 +187,6 @@ impl Web3Rpc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let tx_id_sender = if config.subscribe_txs {
|
let tx_id_sender = if config.subscribe_txs {
|
||||||
// TODO: warn if tx_id_sender is None?
|
|
||||||
tx_id_sender
|
tx_id_sender
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -557,7 +556,8 @@ impl Web3Rpc {
|
|||||||
// trace!("waiting on chain id for {}", self);
|
// trace!("waiting on chain id for {}", self);
|
||||||
let found_chain_id: Result<U64, _> = self
|
let found_chain_id: Result<U64, _> = self
|
||||||
.wait_for_request_handle(&authorization, None, unlocked_provider.clone())
|
.wait_for_request_handle(&authorization, None, unlocked_provider.clone())
|
||||||
.await?
|
.await
|
||||||
|
.context(format!("waiting for request handle on {}", self))?
|
||||||
.request(
|
.request(
|
||||||
"eth_chainId",
|
"eth_chainId",
|
||||||
&json!(Option::None::<()>),
|
&json!(Option::None::<()>),
|
||||||
@ -580,18 +580,20 @@ impl Web3Rpc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(anyhow::Error::from(e));
|
return Err(anyhow::Error::from(e)
|
||||||
|
.context(format!("unable to parse eth_chainId from {}", self)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_block_data_limit(&authorization, unlocked_provider.clone())
|
self.check_block_data_limit(&authorization, unlocked_provider.clone())
|
||||||
.await?;
|
.await
|
||||||
|
.context(format!("unable to check_block_data_limit of {}", self))?;
|
||||||
|
|
||||||
drop(unlocked_provider);
|
drop(unlocked_provider);
|
||||||
|
|
||||||
info!("successfully connected to {}", self);
|
info!("successfully connected to {}", self);
|
||||||
} else if self.provider.read().await.is_none() {
|
} else if self.provider.read().await.is_none() {
|
||||||
return Err(anyhow!("failed waiting for client"));
|
return Err(anyhow!("failed waiting for client {}", self));
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -726,7 +728,7 @@ impl Web3Rpc {
|
|||||||
// this does loop. just only when reconnect is enabled
|
// this does loop. just only when reconnect is enabled
|
||||||
#[allow(clippy::never_loop)]
|
#[allow(clippy::never_loop)]
|
||||||
loop {
|
loop {
|
||||||
debug!("subscription loop started");
|
trace!("subscription loop started on {}", self);
|
||||||
|
|
||||||
let mut futures = vec![];
|
let mut futures = vec![];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user