ENS Usernames, Unallowed names & Slashing Conditions

Usernames is a important feature for Status, as it will help users find each other, replacing the need of passing pub-keys or qr codes. Also might be useful for users that want to identify themselves in public chats.

The security concerns with Usernames are the same we have in web2, with characters that are similar but a different bytecode were used in past very creatively by scammers.
Not just this, there’s also some special words, that shouldn’t be held by anyone, such as support.
The main approach to safeguard users is not displaying those names, however is also possible to introduce another protection in the smart contract itself.

The way ENS is designed makes not possible for the contract actually know what names are being registered, instead it works with hashes of those names, so it should never be possible to prevent someone to register anything not registered yet.
However, is possible to be revealed for the smart contract the username, proving it’s against the defined rules, like a slashing mechanism to punish whoever tries to use a illegal username and reward the first reporter.

Currently I’m working on a smart contract for ENS Usernames that have 4 slashing conditions, which I described below.

Slashing conditions

Non alphanumeric characters.

We want to make usernames simple. Why someone would want a username with a complicated character? Probably to scam people or abuse a system.
So the allowed characters are a to z and 0 to 9, everything else, including minus, underline, uppercase characters will produce a username able to be slashed by anyone knowing the offending username.

    /**
     * @notice Slash username that contains a non alphanumeric character.
     * @param _username Raw value of offending username.
     * @param _offendingPos Position of non alphanumeric character.
     */
    function slashInvalidUsername(
        bytes _username,
        uint256 _offendingPos
    ) 
        external
    { 
        require(_username.length > _offendingPos, "Invalid position.");
        byte b = _username[_offendingPos];
        
        require(!((b >= 48 && b <= 57) || (b >= 97 && b <= 122)), "Not invalid character.");
    
        slashUsername(_username);
    }

Less then 5 characters

(carl.stateofus.eth = invalid, alice.stateofus.eth = valid)
Too small usernames are too rare and may cause competition in registering them. Maybe could be less then 4, but I don’t think anyone needs usernames that small.

    /**
     * @notice Slash username smaller then `usernameMinLenght`.
     * @param _username Raw value of offending username.
     */
    function slashSmallUsername(
        bytes _username
    ) 
        external 
    {
        require(_username.length < usernameMinLenght, "Not a small username.");
        slashUsername(_username);
    }

Reserved words

Some common words, specially used in the ecossytem, should be prevented from being registered to avoid scams, such as support, ethnotice, and statusnetwork, and also others that might be potentially interesting for future uses, such as teller or openbounty. Personally I think that status should have its own domain for pointing its services, an leave stateofus.eth just for usernames, however, to still avoid this confusions, we should reserve all words we can think of.
The list can be heavy, as the contents can stored in the smart contract as merkle tree.
The current list of reserved words is available here: https://github.com/status-im/ens-usernames/blob/master/config/ens-usernames/reservedNames.js

    /**
     * @notice Slash usernmae that is exactly a reserved name.
     * @param _username Raw value of offending username.
     * @param _proof Merkle proof that name is listed on merkle tree.
     */
    function slashReservedUsername(
        bytes _username,
        bytes32[] _proof
    ) 
        external 
    {   
        require(
            MerkleProof.verifyProof(
                _proof,
                reservedUsernamesMerkleRoot,
                keccak256(_username)
            ),
            "Invalid Proof."
        );
        slashUsername(_username);
    }

Start with 0x and the 5 following chars are hex

To avoid users registering usernames that look like an address, the contract read if the first part of any supplied username start looking like an address, if so, it can be slashed.


    /**
     * @notice Slash username starting with "0x" and with lenght greater than 12.
     * @param _username Raw value of offending username.
     */
    function slashAddressLikeUsername(
        string _username
    ) 
        external 
    {
        bytes memory username = bytes(_username);
        require(username.length > 12, "Too small to look like an address.");
        require(username[0] == byte("0"), "First character need to be 0");
        require(username[1] == byte("x"), "Second character need to be x");
        slashUsername(username);
    } 

Slashing Consequences

When a username is slashed, it gets the resolver and ownership unset (set to zero) and the funds held by that name are send to the slasher.

   /**
     * @dev Removes account hash of `_username` and send account.balance to msg.sender.
     * @param _username Username being slashed.
     */
    function slashUsername(bytes _username) internal {
        bytes32 label = keccak256(_username);
        bytes32 namehash = keccak256(abi.encodePacked(ensNode, label));
        uint256 amountToTransfer;
        if(accounts[label].creationTime == 0) {
            require(
                ensRegistry.owner(namehash) != address(0) ||
                ensRegistry.resolver(namehash) != address(0),
                "Nothing to slash."
            );
        } else {
            amountToTransfer = accounts[label].balance;
            delete accounts[label];
        }

        ensRegistry.setSubnodeOwner(ensNode, label, address(this));
        ensRegistry.setResolver(namehash, address(0));
        ensRegistry.setOwner(namehash, address(0));
        
        if (amountToTransfer > 0) {
            reserveAmount -= amountToTransfer;
            require(token.transfer(msg.sender, amountToTransfer), "Error in transfer.");   
        }
        emit UsernameOwner(namehash, address(0));
    }

(note this function is internal, called when a slashing condition is met)

Automatic Slashing

The idea is that Status Wallet client and Status Nodes automatically suggest slashing a username when they are revealed to it.

Status Mobile, when a user chats in public chat and reveal their username, or when a user receives a message from a user that reveals it invalid username, a message box will be prompted to user asking them to slash username or broadcast its name to network (for other slashing it).

Status Node, when a user reveals invalid name in public chat or when Status Mobile broadcast a privately revealed invalid username.

We are reserving usernames for people?

No, this is not intended to reserve usernames for people. The contract could be extended to support a feature like this, but that’s not the original reason of introducing this changes.
We could also modify the contract to allow controller to register a reserved username for an address, which then would not be slashable.

What happen to squatted usernames of celebrities?

My personal opinion is “nothing”, but if this starts hurting ecossystem is possible to fix squatted usernames through contract upgrade,
The new contract would be able to clear ENS entries but not take the funds, because they are in the older contract, where anyone then would be able to release their funds (but they wouldn’t need to).
The reserved usernames then would be able to be repurposed by Status Network Democracy.
This

Why not just ignoring those illegal usernames?

The advantage of having the slashing in the smart contract is that interoperable systems that use ENS will also use that rules, would be not just a “cosmetic change”, but ideally the contract don’t allow such registers to exist.
Also, instead of ignoring the invalid usernames, user is alerted and can take an action which will lead to this user profit and attacker loss.

Slashing conditions can change?

Note that Slashing conditions will not be changeable by contract Controller, but an upgrade of the contract could change it, and users that accept that change would be able to migrate their funds and keep the name, or withdraw and release the name, just like in any UsernameRegistrar contract upgrade. The users also have the option to do nothing and keep using their usernames, specially if they don’t hurt the “new agreement”.
This was important to ensure safety of funds of community at any circumstance, therefore the way is designed it would be impossible to offend the proposed terms, even if is the will of the Controller, and also don’t become a trouble to normal users.

I want the opinion of the community about the slashing conditions and what else we can make to make usernames safer to everyone.
Also a help in reviewing the reserved usernames, specially the part with crypto community related names.

4 Likes

I love the idea of incentivizing users to police each other (can’t believe I just wrote that…)

I noticed in reservedNames.js there was a list of national languages but not a list of nations. Might make sense to restrict registering names of nations as well (northkorea dprk) and names of companies (microsoft apple)

What’s immediately evident is that there’s no way we can preemptively list all the names that can be malicious. Upgrading a contract just to add new names seems like overkill, what do you think of adding a governed mechanism to add new entries to reservedSubdomainsMerkleRoots ?

ALSO: I think a list of restricted names would benefit not only Status but any other organization looking to build on ENS, and perhaps even other name services like HNS. The first thing that comes to mind is a TCR that an organization can join to maintain a global list of restricted names. We could reach out to other projects and get their thoughts on this.

I think that ‘microsoft’ and ‘apple’ would fall into reserving names for people, and I want my name reserved too :slight_smile:

It’s easy to implement a simple function in the contract that changes the merkle root or add new elements to an array of merkle roots that contains the reserved names, however doing so we should not slash offending usernames that were registered when it was not illegal (because it hurts agreement), therefore things don’t get so simple anymore.

The global TCR would be interesting, we could migrate to the rules be chosen from a InterDAO decision, but the initial plan is that it’s kind of hardcoded and changing it is designed to be difficult.

The difficulty is introduced to require a bigger effort in study and coordination with next update, where the rules could be like “easy restrict new usernames” by DAO voting.
Also I don’t see any reason why the reserved list can be well built before the contract is deployed. Even then someone will see… “Hey, they just forgot to reserve X word, haha I will register it.” but if we forgot it probably was just a squatting.

Thanks so much for the thorough write-up, @ricardo3.

A couple questions:

  1. If we also implement these restrictions on the client side (e.g. no special characters), what’s the use of slashing? Wouldn’t it be impossible for a user to register jarrad-hope.stateofus.eth? Is it simply to encourage a standard across registries so that “bad” ENS names can be prevented across the ecosystem?

  2. If an invalid name can be automatically detected, why can’t it also be automatically slashed? Why does a user need to take the action? Simply to redistribute the funds somewhere?

I like @arseniy’s points about the reserved list as well.

I think it seems tricky to create one arbitrarily ourselves, so our criteria for restricting a name need to be well defined. For instance, what justification do we have for reserving words as general as friends or idea?

  1. It would be still possible to register the names interacting directly to the smart contract or through a hacked UI, and the “invalid” usernames are still resolvable in apps that would not properly sanitize the names.
    Having the slashing mechanism we assert that those usernames were never actually used, neither outside the ranges of our UI restrictions.
    For example, while our UI would block register and display ‘ricardo-schmidt’ but other software might allow it because they have more loose restrictions, so it would work outside Status. This way it would assert it’s not even possible for other wallet or a Status update to start supporting that characters, because the contract blocks it.

  2. For Status Node (and Status Desktop), the slashing can be automatic, because we can unlock a private key to that purpose and let the client running 24h/7 willing to slash usernames along with other “services” those nodes providing.
    For Status Wallet, users need to unlock the account to sign the slashing transaction, and maybe the user don’t want to take the work, if they don’t sign it they broadcast the invalid username to a channel where Status Nodes are listening for slashing.

The reserved names @arseniy suggested are company names, therefore fit into a “for persons” purpose, while ‘friends’ and ‘idea’ are general words that don’t translate any person identity.
Why someone would want their username to be ‘friends’? The reason ‘friends’ is good to be reserved, is because it could be used by a scammer to try impersonate some network bot.
Many words are reserved for unknown unknowns, and the more words we can restrict, the better, but we should not restrict the whole dictionary because this also makes no sense. Just words that can resemble network/social utility.

I don’t believe those reserved name lists will end up being perfect, I suggest a initial research for words we don’t want users to register, and the justification is generally because “security”. There are infinite options of names, why someone want to have the name as ‘statusnetwork.stateofus.eth’?
If other words show to become a huge problem, because we forgot to restrict some special words, Status Network can take action, but I don’t believe that the list we initially define would ever change, specially because it requires a contract upgrade (50% quorum of SNT influence in Status Network Democracy), specifically meant to make difficult the change this list to discourage restricting names for particular reasons (for people) and censorship.

The big moral question is not if we initially reserve usernames, but if we change that list and this affect the sovereign of users. While the contract upgrade is available, which I think should forever be, there is capability of network voting for any change, however I don’t think we could reach enough incentive for upgrading this to add a word in the list, this incentive would grow if the upgrade would solve a problem.
At end, if we want to guarantee the user forever owns the username with 0 capability of network action, we need to remove upgradability, but then if some problem happens we give no option to network to fix itself features.

@arseniy about company names in reserved words, while I think they are “for persons”, I see there could be use for reserving popular company names, services, and even governament names - because they also could be used to very creative specific scams.
For example, let’s say everyone starts using Status, and a bad actor, the owner of santander.stateofus.eth starts messaging lots users with scammy messages and people get hurted, for example, announcing an ICO or crypto features that are a fraud, etc.
It would not hurt to reserve those words, and I don’t think those companies want to use usernames of Status, these companies should use their own ENS domain name.

I think it’s wise/useful to have a back-door, especially with unknown unknowns. Just need to make it transparent. Scammy message is a problem the whole community will have. Hopefully, for companies, that problem will be solved by mapping thier eth addresses to their DNS coupled with a form of community curation.

We should add some company names before going mainnet, I suggest famous tech and finance companies to have their names reserved.

In case we miss something and someone abuse the username feature, Controller of contract can migrate to a new version that contains that additional reserved words, which then anyone would be able to clear those usernames, or in case of “unregistered usernames” but “ens registered” (registered in older contract) then the Controller would be able to control those reserved subdomains.

The reason for not including a changeable reserved names in the contract is, in order to keep user terms, the complexity would become too high, and the migration process is not required for registered users in past versions, as they keep the old terms.

Listened to epicentre with radical markets writer: https://epicenter.tv/episode/251/

Seems this could be a good application of COST: https://github.com/ethereum/EIPs/issues/1211

1 Like

One month later… makes much more sense, thank you @ricardo3.

@petty what do you think about adding these company names to our list of reserved names for security purposes? The current list of reserved words is here: https://github.com/status-im/ens-usernames/blob/master/config/ens-usernames/reservedNames.js

While it helps, it won’t cover all of the possibilities. I see no problem in these names being added.

HI @ricardo3, would you like to see the contents of this thread posted to our status, with a link to the discuss post, to potentially get wider discussion? Here is a draft that we could work from:

https://our.status.im/p/a0ebe389-7b92-496f-9c47-1973b123d4f3/

Yes, we can start a discuss on our, but only regarding “Unallowed names”, because the other slashing conditions are already defined.