fix(providers): validate address resolver (#1605)
* validate address resolver * use Selector type instead * specify which ens_name on resolver error * pad supportsInterface argument
This commit is contained in:
parent
ffcf10d6b3
commit
d22ab5de0a
|
@ -28,6 +28,9 @@ pub const NAME_SELECTOR: Selector = [105, 31, 52, 49];
|
|||
/// text(bytes32, string)
|
||||
pub const FIELD_SELECTOR: Selector = [89, 209, 212, 60];
|
||||
|
||||
/// supportsInterface(bytes4 interfaceID)
|
||||
pub const INTERFACE_SELECTOR: Selector = [1, 255, 201, 167];
|
||||
|
||||
/// Returns a transaction request for calling the `resolver` method on the ENS server
|
||||
pub fn get_resolver<T: Into<Address>>(ens_address: T, name: &str) -> TransactionRequest {
|
||||
// keccak256('resolver(bytes32)')
|
||||
|
@ -39,6 +42,19 @@ pub fn get_resolver<T: Into<Address>>(ens_address: T, name: &str) -> Transaction
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a transaction request for checking interface support
|
||||
pub fn supports_interface<T: Into<Address>>(
|
||||
resolver_address: T,
|
||||
selector: Selector,
|
||||
) -> TransactionRequest {
|
||||
let data = [&INTERFACE_SELECTOR[..], &selector[..], &[0; 28]].concat();
|
||||
TransactionRequest {
|
||||
data: Some(data.into()),
|
||||
to: Some(NameOrAddress::Address(resolver_address.into())),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a transaction request for calling
|
||||
pub fn resolve<T: Into<Address>>(
|
||||
resolver_address: T,
|
||||
|
|
|
@ -1263,6 +1263,11 @@ impl<P: JsonRpcClient> Provider<P> {
|
|||
return Err(ProviderError::EnsError(ens_name.to_string()))
|
||||
}
|
||||
|
||||
if let ParamType::Address = param {
|
||||
// Reverse resolver reverts when calling `supportsInterface(bytes4)`
|
||||
self.validate_resolver(resolver_address, selector, ens_name).await?;
|
||||
}
|
||||
|
||||
// resolve
|
||||
let data = self
|
||||
.call(&ens::resolve(resolver_address, selector, ens_name, parameters).into(), None)
|
||||
|
@ -1271,6 +1276,39 @@ impl<P: JsonRpcClient> Provider<P> {
|
|||
Ok(decode_bytes(param, data))
|
||||
}
|
||||
|
||||
/// Validates that the resolver supports `selector`.
|
||||
async fn validate_resolver(
|
||||
&self,
|
||||
resolver_address: Address,
|
||||
selector: Selector,
|
||||
ens_name: &str,
|
||||
) -> Result<(), ProviderError> {
|
||||
let data =
|
||||
self.call(&ens::supports_interface(resolver_address, selector).into(), None).await?;
|
||||
|
||||
if data.is_empty() {
|
||||
return Err(ProviderError::EnsError(format!(
|
||||
"`{}` resolver ({:?}) is invalid.",
|
||||
ens_name, resolver_address
|
||||
)))
|
||||
}
|
||||
|
||||
let supports_selector = abi::decode(&[ParamType::Bool], data.as_ref())
|
||||
.map(|token| token[0].clone().into_bool().unwrap_or_default())
|
||||
.unwrap_or_default();
|
||||
|
||||
if !supports_selector {
|
||||
return Err(ProviderError::EnsError(format!(
|
||||
"`{}` resolver ({:?}) does not support selector {}.",
|
||||
ens_name,
|
||||
resolver_address,
|
||||
hex::encode(&selector)
|
||||
)))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Anvil and Ganache-only function for mining empty blocks
|
||||
pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> {
|
||||
|
@ -2133,4 +2171,19 @@ mod tests {
|
|||
assert_eq!(tx.gas_price(), Some(gas_price));
|
||||
assert!(tx.access_list().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mainnet_lookup_address_invalid_resolver() {
|
||||
let provider = crate::MAINNET.provider();
|
||||
|
||||
let err = provider
|
||||
.lookup_address("0x30c9223d9e3d23e0af1073a38e0834b055bf68ed".parse().unwrap())
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
&err.to_string(),
|
||||
"ens name not found: `ox63616e.eth` resolver (0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) is invalid."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue