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:
joshieDo 2022-08-17 22:13:51 +01:00 committed by GitHub
parent ffcf10d6b3
commit d22ab5de0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 0 deletions

View File

@ -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,

View File

@ -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."
);
}
}