Skip to content

Commit

Permalink
feat(ens): New error to distinguish between registry.resolve() return…
Browse files Browse the repository at this point in the history
…ing empty address and failing, improved tests
  • Loading branch information
gaspermerela committed Dec 12, 2023
1 parent 15fd01d commit 6bb31b7
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 12 deletions.
33 changes: 27 additions & 6 deletions ethers-ens/src/main/kotlin/io/ethers/EnsResolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.ethers
import io.ethers.EnsResolver.Error.FailedToResolve
import io.ethers.EnsResolver.Error.UnknownResolver
import io.ethers.core.ENSRegistryWithFallback
import io.ethers.core.FastHex
import io.ethers.core.PublicResolver
import io.ethers.core.types.Address
import io.ethers.core.types.BlockId
Expand Down Expand Up @@ -34,7 +35,7 @@ class EnsResolver(private val provider: Provider) {
return resolver.addr(Bytes(nameHash))
.call(BlockId.LATEST)
.sendAwait()
.mapError { FailedToResolve(resolver.address, ensName, it) }
.mapError { FailedToResolve(resolver.address, ensName) }
}

/**
Expand All @@ -54,8 +55,16 @@ class EnsResolver(private val provider: Provider) {
val registryContract = ENSRegistryWithFallback(provider, registryAddr)
return registryContract.resolver(Bytes(nameHash))
.call(BlockId.LATEST)
.map {
//
if (it == Address.ZERO) {
return@map RpcResponse.error(Error.ResolvingResolver(registryAddr, nameHash.toString()))
} else {
return@map RpcResponse.result(it)
}
}
.sendAwait()
.mapError { UnknownResolver(it) }
.mapError { UnknownResolver(registryAddr, FastHex.encodeWithPrefix(nameHash)) }
}

/**
Expand Down Expand Up @@ -85,14 +94,27 @@ class EnsResolver(private val provider: Provider) {
}
}

/**
* Error on resolver address resolving.
*/
data class ResolvingResolver(
val registryAddress: Address,
val nameHash: String,
) : Error() {
override fun doThrow() {
throw RuntimeException("Error when resolving resolver on registry $registryAddress for nameHash $nameHash.")
}
}

/**
* Resolver address not found for given nameHash on registry.
*/
data class UnknownResolver(
val cause: RpcResponse.Error,
val registryAddress: Address,
val nameHash: String,
) : Error() {
override fun doThrow() {
throw RuntimeException("Resolver for given nameHash was not found: $cause")
throw RuntimeException("Resolver on registry $registryAddress for nameHash $nameHash was not found.")
}
}

Expand All @@ -102,11 +124,10 @@ class EnsResolver(private val provider: Provider) {
data class FailedToResolve(
val resolverAddr: Address,
val ensName: String,
val cause: RpcResponse.Error,
) :
Error() {
override fun doThrow() {
throw RuntimeException("Failed to resolve ens name: $ensName with resolver $resolverAddr: $cause")
throw RuntimeException("Failed to resolve ens name: $ensName with resolver $resolverAddr.")
}
}
}
Expand Down
61 changes: 55 additions & 6 deletions ethers-ens/src/test/kotlin/io/ethers/ens/EnsResolverTest.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.ethers.ens

import io.ethers.EnsResolver
import io.ethers.EnsResolver.Companion.resolveName
import io.ethers.core.types.Address
import io.ethers.providers.HttpClient
import io.ethers.providers.Provider
import io.ethers.providers.types.RpcResponse
import io.kotest.core.spec.style.FunSpec
import io.kotest.datatest.withData
import io.kotest.matchers.shouldBe
Expand All @@ -12,37 +14,84 @@ import io.kotest.matchers.types.shouldBeInstanceOf
private const val MAINNET_HTTP_RPC = "https://ethereum.publicnode.com"

class EnsResolverTest : FunSpec({
data class EnsNameTestData(
val ensName: String,
val nameHash: String,
val address: Address = Address.ZERO,
)

context("Ens resolving with instantiated EnsResolver") {

// Todo mock provider
val provider = Provider(HttpClient(MAINNET_HTTP_RPC))
val ensResolver = EnsResolver(provider)

context("Valid ENS names") {
withData(
listOf(
"resolver.eth" to Address("0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63"),
"RESOLVER.eth" to Address("0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63"),
EnsNameTestData(
ensName = "resolver.eth",
nameHash = "0x469fbad6482d86a40a35d188cb7f8256302a5d6c50e9071c4f4e9f7604b2cac8",
address = Address("0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63"),
),
EnsNameTestData(
ensName = "rEsoLvEr.ETh",
nameHash = "0x469fbad6482d86a40a35d188cb7f8256302a5d6c50e9071c4f4e9f7604b2cac8",
address = Address("0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63"),
),
),
) {
ensResolver.resolveName(it.first).resultOrThrow() shouldBe it.second
ensResolver.resolveName(it.ensName).resultOrThrow() shouldBe it.address

provider.resolveName(it.ensName).resultOrThrow() shouldBe it.address
}
}

// ERROR TESTING

/**
* Testing [EnsResolver.Error.EnsNameInvalid]
*/
test("Invalid ENS names") {
ensResolver.resolveName("").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()
ensResolver.resolveName("\t").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()
ensResolver.resolveName(".").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()
ensResolver.resolveName("\n.").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()

provider.resolveName("").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()
provider.resolveName("\t").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()
provider.resolveName(".").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()
provider.resolveName("\n.").error.shouldBeInstanceOf<EnsResolver.Error.EnsNameInvalid>()
}

/**
* Testing [EnsResolver.Error.Normalisation]
*/
test("Failed normalisation") {
ensResolver.resolveName("xn--u-ccb.com").error.shouldBeInstanceOf<EnsResolver.Error.Normalisation>()

provider.resolveName("xn--u-ccb.com").error.shouldBeInstanceOf<EnsResolver.Error.Normalisation>()
}

/**
* Testing [EnsResolver.Error.UnknownResolver]
*/
context("Non-existent ens names") {
fun testError(error: RpcResponse.Error?, testData: EnsNameTestData) {
error.shouldBeInstanceOf<EnsResolver.Error.UnknownResolver>()
error.nameHash shouldBe testData.nameHash
error.registryAddress shouldBe EnsResolver.getRegistryAddress(provider.chainId)
}

withData(
listOf(
"123.kriptal.eth",
EnsNameTestData(
ensName = "123.kriptal.eth",
nameHash = "0x469fbad6482d86a40a35d188cb7f8256302a5d6c50e9071c4f4e9f7604b2cac8",
),
),
) {
ensResolver.resolveName(it).error.shouldBeInstanceOf<EnsResolver.Error.FailedToResolve>()
testError(ensResolver.resolveName(it.ensName).error, it)
testError(provider.resolveName(it.ensName).error, it)
}
}
}
Expand Down

0 comments on commit 6bb31b7

Please sign in to comment.