This is a read note of Mastering Ethereum Ch12: Decentralized Applications (DApps). Web3 DApps are about decentralizing all aspects of an application: logic, payment, storage, messaging, naming, etc.
A DApp is an application that is mostly or entirely decentralized in some aspects of:
- application logic: immutable, removable smart contracts in all nodes. Very expensive to create and run. May work with off-chain computation.
- frontend software: mobile light clients.
- data storage: expensive on-chain. May work with off-chain centralized (RDB) or P2P storage platform (IPFS or Swarm).
- messsage communication: exchange messages between applications, between different instances of the application, or between users of the application.
- name resolution
Three advantages of DApp are: resiliency, transparency, and censorship resistance.
When building a DApp, you have to decide if you want to make the smart contracts truly independent, launching them and then having no control, or create privileged accounts and run the risk of those being compromised. Either choice carries risk, but in the long run, true DApps cannot have specialized access for privileged accounts—that’s not decentralized.
2 The Ethereum Name Service (ENS)
Name registration was the first noncurrency application of blockchains. ENS offers a decentralized name service. Furthermore, ENS is supported by a number of DApps for registration, management, and auctions of registered names.
ENS is specified mainly in three Ethereum Improvement Proposals: EIP-137, which specifies the basic functions of ENS; EIP-162, which describes the auction system for the .eth root; and EIP-181, which specifies reverse resolution of addresses. ENS follows a “sandwich” design philosophy: a very simple layer on the bottom, followed by layers of more complex but replaceable code, with a very simple top layer that keeps all the funds in separate accounts.
2.1 Bottom Layer: Name Owners and Resolvers
The ENS operates on “nodes” instead of human-readable names: a human-readable name is converted to a node using the “Namehash” algorithm. The base layer of ENS is a cleverly simple contract (less than 50 lines of code) defined by ERC137 that allows only nodes' owners to set information (the resolver, time to live, or transferring the ownership) about their names and to create subnodes (the ENS equivalent of DNS subdomains).
Namehash recursively hashes components of the name, producing a unique, fixed-length string (or “node”) for any valid input domain. For example, the Namehash node of
keccak('<example.eth>' node) + keccak('<subdomain>'). The subproblem we must solve is to compute the node for
example.eth, which is
keccak('<.eth>' node) + keccak('<example>'). To begin, we must compute the node for
eth, which is
keccak(<root node>) + keccak('<eth>'). The root node is the “base case” of our recursion, and we can’t define it recursively. The root node is defined as
0x0000000000000000000000000000000000000000000000000000000000000000 that is
32 zero bytes. Putting this all together, the node of
subdomain.example.eth is therefore
keccak(keccak(keccak(0x0...0 + keccak('eth')) + keccak('example')) + keccak('subdomain')).
The root node is controlled by a
4-of-7 multisig, held by people in different countries (built as a reflection of the
7 keyholders of the DNS system). The keyhoalders allow adding new top level domains (TLDs), if the community agrees they are needed. They also work in consensus to migrate the ownership of the root multisig to a more decentralized contract, when such a system is agreed upon, tested, and implemented.
The basic ENS contract can’t add metadata to names; that is the job of so-called “resolver contracts.” These are user-created contracts that can answer questions about the name, such as what Swarm address is associated with the app, what address receives payments to the app (in ether or tokens), or what the hash of the app is (to verify its integrity).
2.2 Middle Layer: The
Currently, the only top-level domain that is uniquely registrable in a smart contract is
.eth domains are distributed via an auction system. Names are distributed via a modified
Vickrey auction. In a traditional Vickrey auction, every bidder submits a sealed bid, and all of them are revealed simultaneously, at which point the highest bidder wins the auction but only pays the second-highest bid.
On a blockchain, some changes are required:
- To ensure bidders don’t submit bids they have no intention of paying, they must lock up a value equal to or higher than their bid beforehand, to guarantee the bid is valid.
- Because you can’t hide secrets on a blockchain, bidders must execute at least two transactions (a commit–reveal process), in order to hide the original value and name they bid on.
- Since you can’t reveal all bids simultaneously in a decentralized system, bidders must reveal their own bids themselves; if they don’t, they forfeit their locked-up funds. Without this forfeit, one could make many bids and choose to reveal only one or two, turning a sealed-bid auction into a traditional increasing price auction.
2.3 Top Layer: The Deeds
The top layer of ENS is yet another super-simple contract with a single purpose: to hold the funds. When you win a name, the funds are not actually sent anywhere, but are just locked up for the period you want to hold the name (at least a year). This works like a guaranteed buyback: if the owner does not want the name any more they can sell it back to the system and recover their ether.
Having a single contract hold millions of dollars in ether has proven to be very risky, so instead ENS creates a deed contract for each new name. The deed contract is very simple (about 50 lines of code), and it only allows the funds to be transferred back to a single account (the deed owner) and to be called by a single entity (the registrar contract).
2.4 ENS Resolvers
In ENS, resolving a name is a two-step process:
- The ENS registry is called with the name to resolve after hashing it. If the record exists, the registry returns the address of its resolver.
- The resolver is called, using the method appropriate to the resource being requested. The resolver returns the desired result.
This two-step process has several benefits. Separating the functionality of resolvers from the naming system itself gives us a lot more flexibility. For convenience, there is a default public resolver that can resolve a variety of resources, including the address (for wallets or contracts) and content (a Swarm hash for DApps or contract source code).