Support IPLD Contenthash

:pray: Sorry for late party…
We’re simplifying our design with IPNS+IPLD and IPNS+IPFS support so we can skip direct ENS+IPLD support for now…

tldr; ENS records for domain.eth is stored in gateway.tld/ipns/f<hash>/.well-known/eth/domain/<record>.json for CCIP read.

It’s fully backwards compatible without any breaking changes to active ENSIPs or contenthash format, it works for both IPNS+IPFS and IPNS+IPLD.

IPFS directory to IPLD json format,
example :

{
    ".well-known": {
        "eth": {
            "domain": {
                "contenthash.json": {
                    "data": "<abi.encode(contenthash)>"
                },
                "_addr": {
                    "60.json": {
                        "data": "<abi.encode(eth_address)>"
                    }
                },
                "avatar.json": {
                    "data": "<abi.encode(string('eip155:1/erc1155:0xb32979486938aa9694bfc898f35dbed459f44424/10063')>"
                }
            }
        }
    }
}

Off-chain records Manager/Resolver contract (WIP),

still missing final signature format/validation.

DNS decode

    uint256 index = 1; // domain level index
    uint256 i = 1; // counter
    uint256 len = uint8(bytes1(name[:1])); // length of label
    bytes[] memory _labels = new bytes[](42); // maximum 42 allowed levels in sub.sub...domain.eth
    _labels[0] = name[1:i += len];
    string memory _path = string(_labels[0]); // suffix after '/.well-known/'
    string memory _domain = _path; // full domain as string

    /// @dev DNSDecode()
    while (name[i] > 0x0) {
        len = uint8(bytes1(name[i:++i]));
        _labels[index] = name[i:i += len];
        _domain = string.concat(_domain, ".", string(_labels[index]));
        _path = string.concat(string(_labels[index]), "/", _path);
        ++index;
    }

    // check if the name contains .eth as root
    // bool dotETH = (keccak256(abi.encodePacked(bytes32(0), keccak256(_labels[index - 1]))) == roothash);

    bytes32 _node;
    bytes32 _namehash = keccak256(abi.encodePacked(bytes32(0), keccak256(bytes(_labels[--index])))); // MUST be equal to roothash of '.eth'
    bytes memory _ipns; // contenthash
    while (index > 0) {
        _namehash = keccak256(abi.encodePacked(_namehash, keccak256(bytes(_labels[--index]))));
        if (contenthash[_namehash].length != 0) {
            _ipns = contenthash[_namehash];
            _node = _namehash;
        }
    }
    
    // require(_node == bytes32(data[4:36]), "BAD_NAMEHASH");

bytes4 selector to file name:

    bytes4 func = bytes4(data[:4]);

    string memory _pathJSON;

    if (bytes(funcToFile[func]).length > 0) {
        _pathJSON = funcToFile[func];
    } else if (func == iResolver.text.selector) {
        _pathJSON = abi.decode(data[36:], (string));
    } else if (func == iOverloadResolver.addr.selector) {
        _pathJSON = string.concat("_addr/", uintToNumString(abi.decode(data[36:], (uint256))));
    } else if (func == iResolver.interfaceImplementer.selector) {
        _pathJSON =
            string.concat("_interface/", bytes2HexString(abi.encodePacked(abi.decode(data[36:], (bytes4))), 0));
    } else if (func == iResolver.ABI.selector) {
        // recheck this
        _pathJSON = string.concat("_abi/", uintToNumString(abi.decode(data[36:], (uint256))));
    } else if (func == iResolver.dnsRecord.selector) {
        (bytes32 _name, uint16 resource) = abi.decode(data[36:], (bytes32, uint16));
        _pathJSON =
            string.concat("_dns/", bytes2HexString(abi.encodePacked(_name), 0), "/", uintToNumString(resource));
    } else {
        revert NotImplemented(func);
    }

IPNS Keys :

Deterministic IPNS keys are generated using domain info, owner info text as part of deterministic signature request (SIWx) *we’ve issues with ABNF URI validation for DApps with multi-entry points as SIWx requires fixed URI, so it’s normal signature request instead of full SIWx feature in wallets.

It’s part of SIWx + HKDF that we’re working on to remix all ENS, ETH/L2s, Nostr & IPFS/IPNS as alternative to Whisper (shh) messaging protocol, e.g., our https://dostr.eth.limo using SIWx in Nostr Client to generate deterministic schnorr/secp256k1 keys.

2 Likes