web3-proxy/web3_proxy/src/block_number.rs

210 lines
7.5 KiB
Rust
Raw Normal View History

//! Helper functions for turning ether's BlockNumber into numbers and updating incoming queries to match.
2022-09-22 02:50:55 +03:00
use anyhow::Context;
use ethers::{
prelude::{BlockNumber, U64},
types::H256,
};
2022-08-10 05:37:34 +03:00
use tracing::warn;
2022-09-22 02:50:55 +03:00
use crate::rpcs::connections::Web3Connections;
2022-08-10 05:37:34 +03:00
pub fn block_num_to_u64(block_num: BlockNumber, latest_block: U64) -> (bool, U64) {
match block_num {
2022-08-23 23:45:00 +03:00
BlockNumber::Earliest => {
// modified is false because we want the backend to see "pending"
(false, U64::zero())
}
2022-08-10 05:37:34 +03:00
BlockNumber::Latest => {
// change "latest" to a number
2022-08-23 23:45:00 +03:00
// modified is true because we want the backend to see the height and not "latest"
2022-08-10 05:37:34 +03:00
(true, latest_block)
}
2022-08-23 23:45:00 +03:00
BlockNumber::Number(x) => {
// we already have a number
(false, x)
}
2022-08-10 05:37:34 +03:00
BlockNumber::Pending => {
// TODO: think more about how to handle Pending
2022-08-23 23:45:00 +03:00
// modified is false because we want the backend to see "pending"
2022-08-10 05:37:34 +03:00
(false, latest_block)
}
}
}
/// modify params to always have a block number and not "latest"
2022-09-22 02:50:55 +03:00
pub async fn clean_block_number(
2022-08-10 05:37:34 +03:00
params: &mut serde_json::Value,
block_param_id: usize,
latest_block: U64,
2022-09-22 02:50:55 +03:00
rpcs: &Web3Connections,
2022-08-10 05:37:34 +03:00
) -> anyhow::Result<U64> {
match params.as_array_mut() {
2022-09-10 03:58:33 +03:00
None => {
// TODO: this needs the correct error code in the response
Err(anyhow::anyhow!("params not an array"))
}
2022-08-10 05:37:34 +03:00
Some(params) => match params.get_mut(block_param_id) {
None => {
if params.len() != block_param_id - 1 {
2022-09-10 03:58:33 +03:00
// TODO: this needs the correct error code in the response
2022-08-10 05:37:34 +03:00
return Err(anyhow::anyhow!("unexpected params length"));
}
// add the latest block number to the end of the params
params.push(serde_json::to_value(latest_block)?);
Ok(latest_block)
}
Some(x) => {
// convert the json value to a BlockNumber
2022-09-22 02:50:55 +03:00
let (modified, block_num) = if let Some(obj) = x.as_object_mut() {
// it might be a Map like `{"blockHash": String("0xa5626dc20d3a0a209b1de85521717a3e859698de8ce98bca1b16822b7501f74b")}`
if let Some(block_hash) = obj.remove("blockHash") {
2022-09-22 02:50:55 +03:00
let block_hash: H256 =
serde_json::from_value(block_hash).context("decoding blockHash")?;
let block = rpcs.block(None, &block_hash, None).await?;
2022-09-22 02:50:55 +03:00
let block_num = block
.number
.expect("blocks here should always have numbers");
// always set modfied to true because we used "take" above
(true, block_num)
} else {
2022-09-22 02:50:55 +03:00
return Err(anyhow::anyhow!("blockHash missing"));
}
} else {
2022-09-22 02:50:55 +03:00
// it might be a string like "latest" or a block number
// TODO: "BlockNumber" needs a better name
let block_number = serde_json::from_value::<BlockNumber>(x.take())?;
2022-08-10 05:37:34 +03:00
2022-09-22 02:50:55 +03:00
let (_, block_num) = block_num_to_u64(block_number, latest_block);
// always set modfied to true because we used "take" above
(true, block_num)
};
2022-08-10 05:37:34 +03:00
// if we changed "latest" to a number, update the params to match
if modified {
*x = serde_json::to_value(block_num)?;
}
Ok(block_num)
}
},
}
}
2022-09-22 02:50:55 +03:00
// TODO: change this to also return the hash needed?
pub async fn block_needed(
2022-08-10 05:37:34 +03:00
method: &str,
params: Option<&mut serde_json::Value>,
2022-09-07 06:54:16 +03:00
head_block_num: U64,
2022-09-22 02:50:55 +03:00
rpcs: &Web3Connections,
2022-08-10 05:37:34 +03:00
) -> Option<U64> {
2022-08-23 23:45:00 +03:00
// if no params, no block is needed
2022-08-10 05:37:34 +03:00
let params = params?;
2022-08-23 23:45:00 +03:00
// get the index for the BlockNumber or return None to say no block is needed.
// The BlockNumber is usually the last element.
2022-08-10 05:37:34 +03:00
// TODO: double check these. i think some of the getBlock stuff will never need archive
let block_param_id = match method {
"eth_call" => 1,
"eth_estimateGas" => 1,
"eth_getBalance" => 1,
"eth_getBlockByHash" => {
// TODO: double check that any node can serve this
return None;
}
"eth_getBlockByNumber" => {
// TODO: double check that any node can serve this
return None;
}
"eth_getBlockTransactionCountByHash" => {
// TODO: double check that any node can serve this
return None;
}
"eth_getBlockTransactionCountByNumber" => 0,
"eth_getCode" => 1,
"eth_getLogs" => {
let obj = params[0].as_object_mut().unwrap();
if let Some(x) = obj.get_mut("fromBlock") {
let block_num: BlockNumber = serde_json::from_value(x.clone()).ok()?;
2022-09-07 06:54:16 +03:00
let (modified, block_num) = block_num_to_u64(block_num, head_block_num);
2022-08-10 05:37:34 +03:00
if modified {
*x = serde_json::to_value(block_num).unwrap();
}
2022-09-22 02:50:55 +03:00
// TODO: maybe don't return. instead check toBlock too?
// TODO: if there is a very wide fromBlock and toBlock, we need to check that our rpcs have both!
2022-08-10 05:37:34 +03:00
return Some(block_num);
}
if let Some(x) = obj.get_mut("toBlock") {
let block_num: BlockNumber = serde_json::from_value(x.clone()).ok()?;
2022-09-07 06:54:16 +03:00
let (modified, block_num) = block_num_to_u64(block_num, head_block_num);
2022-08-10 05:37:34 +03:00
if modified {
*x = serde_json::to_value(block_num).unwrap();
}
return Some(block_num);
}
2022-09-22 02:50:55 +03:00
if obj.contains_key("blockHash") {
1
} else {
return None;
2022-08-10 05:37:34 +03:00
}
}
"eth_getStorageAt" => 2,
"eth_getTransactionByHash" => {
// TODO: not sure how best to look these up
// try full nodes first. retry will use archive
return None;
}
"eth_getTransactionByBlockHashAndIndex" => {
2022-09-05 08:53:58 +03:00
// TODO: check a Cache of recent hashes
2022-08-10 05:37:34 +03:00
// try full nodes first. retry will use archive
return None;
}
"eth_getTransactionByBlockNumberAndIndex" => 0,
"eth_getTransactionCount" => 1,
"eth_getTransactionReceipt" => {
// TODO: not sure how best to look these up
// try full nodes first. retry will use archive
return None;
}
"eth_getUncleByBlockHashAndIndex" => {
2022-09-05 08:53:58 +03:00
// TODO: check a Cache of recent hashes
2022-08-10 05:37:34 +03:00
// try full nodes first. retry will use archive
return None;
}
"eth_getUncleByBlockNumberAndIndex" => 0,
"eth_getUncleCountByBlockHash" => {
2022-09-05 08:53:58 +03:00
// TODO: check a Cache of recent hashes
2022-08-10 05:37:34 +03:00
// try full nodes first. retry will use archive
return None;
}
"eth_getUncleCountByBlockNumber" => 0,
_ => {
// some other command that doesn't take block numbers as an argument
return None;
}
};
2022-09-22 02:50:55 +03:00
match clean_block_number(params, block_param_id, head_block_num, rpcs).await {
2022-08-10 05:37:34 +03:00
Ok(block) => Some(block),
Err(err) => {
// TODO: seems unlikely that we will get here
warn!(?err, "could not get block from params");
None
}
}
}