From d22ab5de0ad47e6840b46c5a8e00876e631aac18 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Wed, 17 Aug 2022 22:13:51 +0100 Subject: [PATCH] fix(providers): validate address resolver (#1605) * validate address resolver * use Selector type instead * specify which ens_name on resolver error * pad supportsInterface argument --- ethers-providers/src/ens.rs | 16 ++++++++++ ethers-providers/src/provider.rs | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/ethers-providers/src/ens.rs b/ethers-providers/src/ens.rs index 8e349ba1..74ad728d 100644 --- a/ethers-providers/src/ens.rs +++ b/ethers-providers/src/ens.rs @@ -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>(ens_address: T, name: &str) -> TransactionRequest { // keccak256('resolver(bytes32)') @@ -39,6 +42,19 @@ pub fn get_resolver>(ens_address: T, name: &str) -> Transaction } } +/// Returns a transaction request for checking interface support +pub fn supports_interface>( + 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>( resolver_address: T, diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index dc07c8fe..d4d0a0eb 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -1263,6 +1263,11 @@ impl Provider

{ 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 Provider

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