RFC5155 for ENS

I was reading RFC5155 which outline DNS Security (DNSSEC) Hashed Authenticated Denial of Existence details how NSEC3 Resource Records in conventional DNS systems are used to securely deny the existence of a domain.

This by listing the types of Resource Records associated with a domain and then linking to the next domain in the zone’s hashed sequence. Here, domain management is conducted through contracts, not the typical DNS zone

Data structure:

This requires a hashing algorithm, hashing iterations, salt, the hashed name of the domain owner, the next hashed domain name in the sequence. Effectively doing such would create a base policy for ENS implementation thus enforcing ENSIP-15.Although it feels close to encroaching a sort of censorship. So where is the line that we should not cross when talking about standardization enforcement normalization policy?

I don’t want to implicate insight from @paulehoffman too much, but maybe you have some good insight on a translation of RFC5155 into an Ethereum / ENS implementation .

Here is what I have discovered so far…

For this (and if I am interpreting this correctly ) to be viable, there must be:

  • a validation function that that checks to ensure whether a name is ENSIP-15 compliant.

  • flagging for non-compliant names, which should be incorporated into the registration process.
    (I can not remember off the top of my head if this is currently a client side rendered indicator or otherwise tucked away somewhere) also to include resolution.

  • like @lightwalker.eth said, event emission, useful for client side checks

When a name is queried, the resolver first checks for compliance before proceeding with the resolution.This is where I think different resolver libraries come into play and other services can be sure on their implementations of ENS will be standard

And of course the fallback solution

Deciding how to handle existing non-compliant names is complex. Options include grandfathering them in, offering a migration path, or invalidating them, each with its own implications.

Definitely need a consensus, proposal and vote on this because stifling innovation with complexities is the opposite of the goal here.


code rough draft

contract ENSHashManager {
    struct NSEC3Record {
        uint8 hashAlgorithm;
        uint16 iterations;
        bytes salt;
        bytes32 hashedOwnerName;
        bytes32 nextHashedOwnerName;
        bytes typeBitMaps;

    mapping(bytes32 => NSEC3Record) private nsec3Records;

    // Hash ENS Name function
    function hashENSName(bytes32 ensName, uint16 iterations, bytes memory salt) public pure returns (bytes32) {
        bytes32 hashedName = keccak256(abi.encodePacked(ensName, salt));
        for (uint16 i = 0; i < iterations; i++) {
            hashedName = keccak256(abi.encodePacked(hashedName, salt));
        return hashedName;

    // Function to add or update NSEC3 Record
    function addOrUpdateNSEC3Record(
        bytes32 ensName, 
        uint8 hashAlgorithm, 
        uint16 iterations, 
        bytes memory salt, 
        bytes memory typeBitMaps, 
        bytes32 nextHashedOwnerName
    ) public {
        bytes32 hashedName = hashENSName(ensName, iterations, salt);

        NSEC3Record memory record = NSEC3Record({
            hashAlgorithm: hashAlgorithm,
            iterations: iterations,
            salt: salt,
            hashedOwnerName: hashedName,
            nextHashedOwnerName: nextHashedOwnerName,
            typeBitMaps: typeBitMaps

        nsec3Records[ensName] = record;

    // Function to authenticate non-existence of a name
    function authenticateNonExistence(
        bytes32 queriedName, 
        uint16 iterations, 
        bytes memory salt, 
        bytes32 startingPoint
    ) public view returns (bool) {
        bytes32 hashedQueriedName = hashENSName(queriedName, iterations, salt);
        bytes32 currentName = startingPoint;

        // Example loop - the actual implementation may vary based on your chain logic
        while (currentName != startingPoint || currentName == bytes32(0)) {
            NSEC3Record memory record = nsec3Records[currentName];

            if (record.hashedOwnerName == hashedQueriedName) {
                return false;

            if (isBetween(hashedQueriedName, record.hashedOwnerName, record.nextHashedOwnerName)) {
                return true;

            currentName = record.nextHashedOwnerName;
            if (currentName == bytes32(0)) {

        return false; // Modify based on your default assumption (existence or non-existence)

    // Utility function to check if a name is between two others
    function isBetween(
        bytes32 hashedName, 
        bytes32 start, 
        bytes32 end
    ) private pure returns (bool) {
        return (start < hashedName) && (hashedName < end);

I’ve moved this to its own topic, because it doesn’t really relate to the topic you posted it in.

5155 / NSEC3 doesn’t really apply to ENS, because the onchain nature of the registry means we already have a complete directory of hashed names thanks to the events the registry emits.

I don’t understand. You have implementations of NSEC3 and references to in many of your code files and the digest in solidity.

This is (or rather, was) for supporting negative DNSSEC proofs when importing DNS names into ENS.