In this chapter we will explore the world of Decentralized Applications or DApps. From the early days of Ethereum, the founders' vision was much broader than "smart contracts": no less than reinventing the web and creating a new world of DApps, aptly called web3. Smart contracts are a way to decentralize the controlling logic and payment functions of applications. Web3 DApps are about decentralizing all other aspects of an application: storage, messaging, naming etc (see Web3: A decentralized web using smart contracts and P2P Technologies).
While "decentralized apps" are an audacious vision of the future, the term "DApp" is often applied to any smart contract with a web front end. Some of these so-called DApps are highly centralized applications (CApps?). Beware of false DApps!
In this section we will develop and deploy a DApp: an auction platform. You can find the source code in the book’s repository under code/auction_dapp. We will look at each apsect of an auction application and see how we can decentralize the application as much as possible.
A Decentralized Application, or DApp, is an application which is mostly or entirely decentralized.
Consider all the possible aspects of an application that may be decentralized:
-
Front-end software
-
Back-end software (application logic)
-
Data storage
-
Name resolution
-
Message communications
Each of these can be somewhat centralized or somewhat decentralized. For example, a front-end can be developed as a web app that runs on a centralized server, or as a mobile app that runs on your device. The back-end and storage can be on private servers and proprietary databases, or a smart contract and P2P storage.
There are many advantages to creating a DApp that a typical centralized architecture can not provide:
1) Resiliency: by having the business-logic controlled by a smart contract, a DApp back-end will be fully distributed and managed on a blockchain platform. Unlike deploying an application on a centralized server, a DApp will have no downtime and will continue to be available as long as the platform is still operating.
2) Transparency: the on-chain nature of a DApp allows everyone to inspect the code and be more sure about its function. Any interaction with the DApp will be stored forever in the blockchain.
3) Censorship Resistance: as long as a user has access to an Ethereum node (running one if necessary), the user will always be able to interact with a DApp without interference from any centralized control. No service provider, or even the owner of the smart contract, can alter the code once it is deployed on the network.
In the Ethereum ecosystem as it stands today, there are very few truly decentralized apps - most still rely on centralized services and servers for some part of their operation. In the future, we expect that it will be possible for every part of any DApp to be operated in a fully decentralized way.
In a DApp, smart contracts are used to store the business logic (program code) and the related state of your application. You can think of a smart contract replacing a server-side (a.k.a. "back-end") component in a regular application. This is an oversimplification, of course. One of the main differences is that any computation executed in a smart contract is very expensive and so should be kept as minimal as possible. It is therefore important to identify which aspects of the application need a trusted and decentralized execution platform.
Ethereum smart contracts allow you to build architectures in which a network of smart contracts call and pass data between each other, reading and writing their own state variables as they go, their complexity restricted only by the block gas limit. After deploying your smart contract, your business logic could well be used by many other developers in the future.
One major consideration of smart contract architecture design is the inability to change the code of a smart contract once it is deployed. It can be deleted if it is programmed with an accessible SELFDESTRUCT opcode, but other than complete removal, the code cannot be changed in any way.
The second major consideration of smart contract architecture design is DApp size; a really large monolithic smart contract may cost a lot of gas to deploy and use. Therefore, some applications may choose to have off-chain computation and an external data source. Keep in mind, however, that having the core business logic of the DApp be dependent on external data (e.g. from a centralized server) means your users will have to trust these external resources.
Unlike the business logic of the DApp, which requires a developer to understand the EVM and new languages such as Solidity, the client side interface of a DApp can use standard web technologies (HTML, CSS, JavaScript, etc). This allows a traditional web developer to use familiar tools, libraries and frameworks. Interactions with Ethereum, such as signing messages, sending transactions and key management are often conducted through the web browser, via an extension such as MetaMask.
Although it is possible to create a mobile DApp as well, currently there are few resources to help create mobile DApp front-ends, mainly due to the lack of mobile clients that can serve as a light client with key management functionality.
The front-end is usually linked to Ethereum via the web3.js JavaScript library, which is bundled with the front-end resources and served to a browser by a web server.
Due to high gas costs and the currently low block gas limit, smart contracts are not suited to store or process large amounts of data. Hence, most DApps will utilize off-chain data storage services, meaning they store the bulky data off the Ethereum chain, on a data storage platform. That data storage platform can be centralized, for example a typical cloud database. Or, the data can be decentralized, stored on a P2P platform such as the Interplanetary File System (IPFS), or Ethereum’s own Swarm platform.
Decentralized P2P storage is ideal for storing and distributing large static assets such as images, videos, and the resources of the application’s front-end web interface (HTML, CSS, JavaScript, etc).
The Inter-Planetary File System (IPFS) is a decentralized content-addressable storage system that distributes stored objects among peers in a P2P network. "Content Addressable" means that each piece of content (file) is hashed and the hash is used to identify that file. You can then retrieve any file from any IPFS node, by requesting it by its hash.
IPFS aims to replace HTTP as the protocol of choice for delivery of web applications. Instead of storing a web application on a single server, the files are stored on IPFS and can be retrieved from any IPFS node.
More information about IPFS can be found at https://ipfs.io
Swarm is another content-addressable P2P storage system, similar to IPFS. Swarm is built by the Ethereum Foundation, as part of the Go-Ethereum suite of tools. Like IPFS, Swarm allows you to store files that get disseminated and replicated by Swarm nodes. You can access any Swarm file by referring to it by a hash. Swarm allows you to access a web site from a decentralized P2P system, instead of a central web server.
The home page for Swarm is itself stored on Swarm and accessible on your Swarm node or a gateway: https://swarm-gateways.net/bzz:/theswarm.eth/
Another major component of any application is inter-process communication. That means being able to exchange messages between applications, between different instances of the application, or between users of the application. Traditionally, this is achieved by reliance on a centralized server.
There are a variety of decentralized alternatives to a server-based protocols, offering messaging over a P2P network. The most notable P2P messaging protocol for DApps is Whisper, which is part of the Ethereum Foundation’s Go-Ethereum suite of tools. More information about Whisper can be found here: https://github.com/ethereum/wiki/wiki/Whisper
In this section we will start building an example DApp, to explore the various decentralization tools. Our DApp will implement a decentralized auction.
The Auction DApp allows a user to register a "deed" token, which represents some unique asset, such as a house, a car, a trademark, etc. Once a token has been registered, the ownership of the token is transferred to the Auction DApp, allowing it to be listed for sale. The Auction DApp lists each of the registered tokens, allowing other users to place bids. During the auction, users can join a chat room, created specifically for each auction. Once an auction is finalized, the deed token ownership is transferred to the winner of the auction.
The overall auction process can be seen in Auction DApp: A simple example auction DApp:
The main components of our Auction DApp are:
-
A smart contract implementing ERC721 non-fungible "deed" tokens (DeedRepository).
-
A smart contract implementing an Auction (AuctionRepository) to sell the deeds.
-
A web front-end using the Vue/Vuetify JavaScript framework.
-
The web3.js library to connect to Ethereum chains (via MetaMask or other clients).
-
A Swarm client, to store resources such as images.
-
A Whisper client, to create per-auction chat rooms for all participants.
You can find the source code for the auction DApp in the book’s repository: https://github.com/ethereumbook/ethereumbook/tree/develop/code/auction_dapp
Our Auction DApp example is supported by two smart contracts that we need to deploy on an Ethereum blockchain in order to support the application: AuctionRepository and DeedRepository.
Let’s start by looking at DeedRepository, shown in DeedRepository.sol : An ERC721 deed token for use in an auction. This contract is an ERC-721 compatible "non-fungible token" (See [erc721]).
link:code/auction_dapp/backend/contracts/DeedRepository.sol[role=include]
As you can see, the DeedRepository contract is a straightforward implementation of an ERC721-compatible token.
Our Auction DApp uses the DeedRepository contract to issue and track tokens for each auction. The auction itself is orchestrated by the AuctionRepository contract, which can be found here:
The AuctionRepository contract is too long to include here in full, but AuctionRepository.sol : The main Auction DApp smart contract is the main definition of the contract and data structures:
contract AuctionRepository {
// Array with all auctions
Auction[] public auctions;
// Mapping from auction index to user bids
mapping(uint256 => Bid[]) public auctionBids;
// Mapping from owner to a list of owned auctions
mapping(address => uint[]) public auctionOwner;
// Bid struct to hold bidder and amount
struct Bid {
address from;
uint256 amount;
}
// Auction struct which holds all the required info
struct Auction {
string name;
uint256 blockDeadline;
uint256 startPrice;
string metadata;
uint256 deedId;
address deedRepositoryAddress;
address owner;
bool active;
bool finalized;
}
The AuctionRepository contract manages all auctions with the following functions:
getCount() getBidsCount(uint _auctionId) getAuctionsOf(address _owner) getCurrentBid(uint _auctionId) getAuctionsCountOfOwner(address _owner) getAuctionById(uint _auctionId) createAuction(address _deedRepositoryAddress, uint256 _deedId, string _auctionTitle, string _metadata, uint256 _startPrice, uint _blockDeadline) approveAndTransfer(address _from, address _to, address _deedRepositoryAddress, uint256 _deedId) cancelAuction(uint _auctionId) finalizeAuction(uint _auctionId) bidOnAuction(uint _auctionId)
We can deploy these contracts to the Ethereum blockchain of our choice (e.g. Ropsten), using truffle in the book’s repository:
cd code/auction_dapp/backend truffle init truffle compile truffle migrate --network ropsten
If you read through the two smart contracts of the Auction DApp you will notice something important:
There is no special account or role that has special privileges over the DApp. Each auction has an owner with some special capabilities, but the Auction DApp itself has no privileged user.
This is a deliberate choice to decentralize the governance of the DApp and relinquish any control once it has been deployed. Some DApps, by comparison, have one or more privileged accounts, with special capabilities, such as the ability to terminate the DApp contract, to override or change its configuration, or to "veto" certain operations. Usually, these governance functions are introduced in the DApp in order to avoid unknown problems that might arise due to a bug.
The issue of governance is a particularly difficult one to solve. It represents a double-edged sword: On one side, privileged accounts are dangerous - if compromised they can subvert the security of the DApp. On the other side, without any privileged account, there are no recovery options if a bug is found. We have seen both of these risks manifest in Ethereum DApps. In the case of The DAO, there were some privileged accounts called the "curators", but they were very limited in their capabilities. Those accounts were not able to override the DAO attacker’s withdrawal of the funds. In a more recent case, the decentralized exchange Bancor experienced a massive theft because a privileged management account was compromised. Turns out, Bancor was not as decentralized as initially assumed.
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 creating privileged accounts and running 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.
Once the Auction DApp contracts are deployed, you can interact with them using your favorite JavaScript console and web3.js, or another web3 library. However, most users will need an easy-to-use interface. Our Auction DApp user interface is built using the as Vue.js JavaScript framework and Vuetify library of UI components.
You can find the user interface code in the code/auction_dapp/frontend folder in the book’s repository. The directory has the following structure and contents:
frontend/ |-- build | |-- build.js | |-- check-versions.js | |-- logo.png | |-- utils.js | |-- vue-loader.conf.js | |-- webpack.base.conf.js | |-- webpack.dev.conf.js | `-- webpack.prod.conf.js |-- config | |-- dev.env.js | |-- index.js | `-- prod.env.js |-- index.html |-- package.json |-- package-lock.json |-- README.md |-- src | |-- App.vue | |-- components | | |-- Auction.vue | | `-- Home.vue | |-- config.js | |-- contracts | | |-- AuctionRepository.json | | `-- DeedRepository.json | |-- main.js | |-- models | | |-- AuctionRepository.js | | |-- ChatRoom.js | | `-- DeedRepository.js | `-- router | `-- index.js
Once you have deployed the contracts, edit the front-end configuration in frontend/src/config.js and enter the address of the DeedRepository and AuctionRepository contracts, as deployed. The front-end application also needs access to an Ethereum node offering a JSON-RPC and websockets interface. Once you’ve configured the front-end, launch it with a web server on your local machine:
npm install npm run dev
The Auction DApp front-end will launch and will be accessible via any web browser at:
If all goes well, you should see Auction DApp User interface which illustrates the Auction DApp running in your web browser:
Our DApp is already quite decentralized, but we can improve things.
The AuctionRepository contract operates independently of any oversight, open to anyone. Once deployed it cannot be stopped, nor can any auction be censored. Each auction has a separate chat room that allows anyone to communicate about the auction without censorship or identification. The various auction assets, such as the description and associated image, are stored on Swarm, making them hard to censor or block.
Anyone can interact with the DApp by constructing transactions manually or by running the Vue frontend on their local machine. The DApp code itself is open source and developed collaboratively on a public repository.
But we can do more to make this DApp decentralized and resilient:
-
Store all the application code on Swarm or IPFS
-
Access the DApp by reference to a name, using the Ethereum Name Service
In the next sections we will further decentralize the Auction DApp.
We introduced Swarm in [swarm], earlier in this chapter. Our Auction DApp already uses Swarm to store the icon image for each auction. This is a much more efficient solution than attempting to store data on Ethereum, which is expensive. It is also a lot more resilient than if these images were stored on a centralized service like a web server or file server.
But we can take things one step further. We can store the entire front-end of the DApp itself in Swarm and run it from a Swarm node directly, instead of running a web server.
To get started, we need to install Swarm and initialize our Swarm node. Swarm is part of the Ethereum Foundation’s Go-Ethereum suite of tools. Refer to the instructions for installing go-ethereum in [go_ethereum_geth], or to install a Swarm binary release, follow these instructions:
Once you have installed Swarm, you can check that it is working correctly by running it with the version command:
$ swarm version Version: 0.3 Git Commit: 37685930d953bcbe023f9bc65b135a8d8b8f1488 Go Version: go1.10.1 OS: linux
To start running Swarm, you must tell it how to connect to an instance of Geth, to access the JSON-RPC API. Get it started by following the instructions here:
When you start swarm, you should see something like this:
Maximum peer count ETH=25 LES=0 total=25 Starting peer-to-peer node instance=swarm/v0.3.1-225171a4/linux-amd64/go1.10 connecting to ENS API url=http://127.0.0.1:8545 swarm[5955]: [189B blob data] Starting P2P networking UDP listener up self=enode://f50c8e19ff841bcd5ce7d2d... Updated bzz local addr oaddr=9c40be8b83e648d50f40ad3d35f... uaddr=e Starting Swarm service 9c40be8b hive starting detected an existing store. trying to load peers hive 9c40be8b: peers loaded Swarm network started on bzz address: 9c40be8b83e648d50f40ad3d35f... Pss started Streamer started IPC endpoint opened url=/home/ubuntu/.ethereum/bzzd.ipc RLPx listener up self=enode://f50c8e19ff841bcd5ce7d2d...
You can confirm that your Swarm node is running correctly by connecting to the local Swarm gateway web interface: http://localhost:8500
You should see a Swarm gateway on localhost and be able to query any Swarm hash or ENS name:
Once you have your local Swarm node and gateway running, you can upload to Swarm and the files will be accessible on any Swarm node, simply by reference to the file hash.
Let’s test this by uploading a file!
$ swarm up code/auction_dapp/README.md ec13042c83ffc2fb5cb0aa8c53f770d36c9b3b35d0468a0c0a77c97016bb8d7c
Swarm has uploaded the README.md file, and returned a hash. We can now use this hash to access the README.md from any Swarm node. For example, we could use the public Swarm gateway:
While uploading one file is relatively straightforward, it is a bit more complex to upload an entire DApp front-end. That’s because the various DApp resources (HTML, CSS, JavaScript, libraries etc.) have embedded references to each other. Normally, a web server translates URLs to local files and serves the correct resources. We can achieve the same for Swarm by packaging our DApp.
In the Auction DApp, there’s a script for packaging all the resources:
$ cd code/auction_dapp/frontend $ npm run build > [email protected] build /home/aantonop/Dev/ethereumbook/code/auction_dapp/frontend > node build/build.js Hash: 9ee134d8db3c44dd574d Version: webpack 3.10.0 Time: 25665ms Asset Size Chunks Chunk Names static/js/vendor.77913f316aaf102cec11.js 1.25 MB 0 [emitted] [big] vendor static/js/app.5396ead17892922422d4.js 502 kB 1 [emitted] [big] app static/js/manifest.87447dd4f5e60a5f9652.js 1.54 kB 2 [emitted] manifest static/css/app.0e50d6a1d2b1ed4daa03d306ced779cc.css 1.13 kB 1 [emitted] app static/css/app.0e50d6a1d2b1ed4daa03d306ced779cc.css.map 2.54 kB [emitted] static/js/vendor.77913f316aaf102cec11.js.map 4.74 MB 0 [emitted] vendor static/js/app.5396ead17892922422d4.js.map 893 kB 1 [emitted] app static/js/manifest.87447dd4f5e60a5f9652.js.map 7.86 kB 2 [emitted] manifest index.html 1.15 kB [emitted] Build complete.
The result of this command will be a new directory code/auction_dapp/frontend/dist that contains the entire Auction DApp frontend, packed together:
dist/ |-- index.html `-- static |-- css | |-- app.0e50d6a1d2b1ed4daa03d306ced779cc.css | `-- app.0e50d6a1d2b1ed4daa03d306ced779cc.css.map `-- js |-- app.5396ead17892922422d4.js |-- app.5396ead17892922422d4.js.map |-- manifest.87447dd4f5e60a5f9652.js |-- manifest.87447dd4f5e60a5f9652.js.map |-- vendor.77913f316aaf102cec11.js `-- vendor.77913f316aaf102cec11.js.map
Now we can upload the entire DApp to Swarm, by using the up command and the recursive option. We will also tell Swarm that index.html is the defaultpath for loading this DApp:
swarm --bzzapi http://localhost:8500 --recursive --defaultpath dist/index.html up dist/ ab164cf37dc10647e43a233486cdeffa8334b026e32a480dd9cbd020c12d4581
Now, our entire Auction DApp is hosted on Swarm and accessible by the Swarm URL:
bzz://ab164cf37dc10647e43a233486cdeffa8334b026e32a480dd9cbd020c12d4581
We’ve made some progress in decentralizing our DApp, but we’ve made it harder to use. A URL like that is much harder to use than a web server with a nice name like auction_dapp.com. Are we forced to sacrifice usability in order to gain decentralization? Not necessarily. In the next section we will examine the Ethereum Naming System (ENS), which allows us to use easy-to-read names but still preserves the decentralized nature of our application.
You can design the best smart contract in the world, but if you don’t provide a good interface for users, they won’t be able to access it.
On the traditional internet, the Domain Name system (DNS), allows us to use human-readable names in the browser, while resolving those names to IP addresses or other identifiers behind the scenes. On the Ethereum blockchain, the Ethereum Naming System (ENS) solves the same problem, but in a decentralized manner.
For example, the Ethereum Foundation donation address is 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359; in a wallet that supports ENS, it’s simply ethereum.eth.
ENS is more than a smart contract. ENS is a fundamental DApp itself, offering a decentralized name service. Furthermore, ENS is supported by a number of DApps for registration, management, and auctions of registered names. ENS demonstrates how DApps can work together: A DApp built to serve other DApps, supported by an ecosystem of DApps, embedded in other DApps, etc.
In this section we will look at how ENS works, how we can set up our own name and link it to a wallet or Ethereum address, how we can embed ENS in another DApp and how we can use ENS to name our DApp resources to make them easier to use.
Name registration was the first non-currency application of blockchains, pioneered by Namecoin. The Ethereum whitepaper gave a two-line Namecoin-like registration system as one of its example applications.
Early releases of Geth and the C++ Ethereum client had a built-in namereg contract (not used any more), and many proposals and ERCs for name services were made, but it was only when Nick Johnson started working for Ethereum Foundation in 2016 and took the project under his wing that serious work on a registrar started.
ENS was launched on Star Wars day, May 4, 2017 (after a failed attempt to launch it on Pi Day, March 15).
ENS is specified mainly in three Ethereum Improvement Proposals (EIPs), namely ERC137, which specifies the basic functions of ENS, ERC162, which describes the auction system for the .eth root, and ERC181, which specifies reverse registration 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.
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 the ENS is a cleverly simple contract (less than 50 lines of code) defined by ERC137 that allows only nodes' owners to set information about their names and to create subnodes (the ENS equivalent of DNS subdomains).
The only functions on the base layer are those that enable a node owner to set information about their own node (specifically the resolver, time to live or transferring the ownership) and to create owners of new subnodes.
Namehash is a recursive algorithm that can convert any name into a hash that identifies the name.
"Recursive" means that we solve the problem by solving a sub-problem that is a smaller problem of the same type, and then use the solution to the sub-problem to solve the original problem.
Namehash recursively hashes components of the name, producing a unique, fixed-length string for any valid input domain, or "node".
For example, the Namehash node of subdomain.example.eth is: keccak'example.eth' node) + keccak('subdomain'
. The sub-problem we must solve is to compute the node for example.eth, which is: keccak'.eth' node) + keccak('example'
. In turn, we must compute the node for eth, which is: keccakRoot Node) + keccak('eth'
.
The root node is what we call the "base case" of our recursion, and we obviously can’t define it recursively, or the algorithm will never terminate! The root node is defined as: 0x0000000000000000000000000000000000000000000000000000000000000000
(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'))
Generalizing, we can define the Namehash function as follows (the base case for the root node, or empty name, followed by the recursive step):
namehash([]) = 0x0000000000000000000000000000000000000000000000000000000000000000 namehash([label, …]) = keccak256(namehash(…) + keccak256(label))
In Python this becomes:
def namehash(name): if name == '': return '\0' * 32 else: label, _, remainder = name.partition('.') return sha3(namehash(remainder) + sha3(label))
Thus, mastering-ethereum.eth will be processed as follows:
namehash('mastering-ethereum.eth') ⇒ sha3(namehash('eth') + sha3('mastering-ethereum')) ⇒ sha3(sha3(namehash('') + sha3('eth')) + sha3('mastering-ethereum')) ⇒ sha3(sha3(('\0' * 32) + sha3('eth')) + sha3('mastering-ethereum'))
Of course, subdomains can themselves have subdomains: after subdomain.example.eth there could be a sub.subdomain.example.eth, then a sub.sub.subdomain.example.eth and so on. To avoid expensive recomputation, since Namehash depends only on the name itself, the node for a given name can be precomputed and inserted into a contract, removing the need for string manipulation, and permitting immediate lookup of ENS records regardless of the number of components in the raw name.
Names consist of a series of dot-separated labels. Although upper and lower case letters are allowed, all labels should follow a UTS46 normalization process that case-folds labels before hashing them, so names with different case but identical spelling will end up with the same Namehash.
You could use labels and domains of any length, but for the sake of compatibility with legacy DNS, the following rules are recommended:
-
Labels should be no more than 64 characters each.
-
Complete ENS names should be no more than 255 characters.
-
Labels should not start or end with hyphens, or start with digits.
One of the results of this hierarchical system is that it relies on the owners of the root node, who are able to create top level domains.
While the eventual goal is to adopt a decentralized decision-making process for new top level domains, right now the root node is controlled by a 4 out of 7 multisig, held by people in different countries (built as a reflection of the seven keyholders of the DNS system). As a result, a majority of at least 4 of the 7 keyholders is required to effect any change.
Currently the purpose and goal of these keyholders is to work in consensus with the community to:
-
Migrate and upgrade the temporary ownership of the .eth TLD (Top Level Domain) to a more permanent contract once the system is evaluated.
-
Allow adding new TLDs, if the community agrees they are needed.
-
Migrate the ownership of the root multisig to a more decentralized contract, when such a system is agreed upon, tested and implemented.
-
Serve as a last resort way to deal with any bugs or vulnerabilities in the top-level registries.
The basic ENS contract can’t add metadata to names; that is the job of "resolver contracts". These are user-created contracts that can answer questions about the name, such as "What’s the Swarm address associated with this app?", "What is the Ethereum address that receives payments in ether and tokens?", or "What’s the hash of the app?" (to verify its integrity).
At the moment, the only top level domain that is uniquely registrable in a smart contract is .eth.
There’s work on enabling traditional DNS domain owners to claim ENS ownership. While in theory this could work for .com the only domain that this has been implemented for so far is [.xyz, and only on Ropsten testnet](https://medium.com/the-ethereum-name-service/how-to-claim-your-dns-domain-on-ens-e600ef2d92ca).
.eth domains are distributed via an auction system. There is no reserved list or priority, and the only way to acquire a name is to use the system. The auction system is a complex piece of code (over 500 lines); most of the early development efforts (and bugs!) in ENS were in this part of the system, but it’s also replaceable and upgradeable (without risk to the funds—more on that later).
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. Therefore bidders are incentivized not to bid less than the true value of the name to them, since bidding their true value increases the chance they will win but does not affect the price they will eventually pay.
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 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.
Therefore, the auction is a four-step process:
-
Start the auction. This is required to broadcast the intent to register a name. This creates all auction deadlines. The names are hashed, so that only those who have the name in their dictionary will know which auction was opened. This allows some privacy, useful if you are creating a new project and don’t want to share details about it. You can open multiple dummy auctions at the same time, so if someone is following you they cannot simply bid on all auctions you open.
-
Make a sealed bid: you must do this before the bidding deadline, by tying a given amount of ether to the hash of a secret message (containing, among others, the hash of the name, the actual amount of the bid, and a salt). You can lock up more ether than you are actually bidding in order to mask your true valuation.
-
Reveal the bid: during the reveal period, you must make a transaction that reveals the bid, which will then calculate the highest bid, the second higher bid and send ether back to unsuccessful bidders. Every time the bid is revealed the current winner is recalculated, therefore the last one to be set before the revealing deadline expires becomes the overall winner.
-
Clean up after: if you are the winner, you can finalize the auction in order to get back the difference between your bid and the second highest. If you forgot to reveal you can make a late reveal and recover a little of your bid.
The top layer of the 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 buy-back: if the owner does not want the name any more they can sell it back to the system and recover their ether (so the cost of holding the name is the opportunity cost of doing something with a return greater than zero).
Of course, 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). This approach drastically reduces the attack surface where bugs can put the funds at risk.
Registering a name in ENS is a four-step process, as we saw in Vickrey Auctions. First we place a bid for any available name, then we reveal our bid after 48 hours to secure the name. ENS timeline for registration is a diagram showing the timeline of registration:
Let’s register our first name!
We will use one of several available user-friendly interfaces to search for available names, place a bid on the name ethereumbook.eth, reveal the bid and secure the name.
There are a number of web-based interfaces to ENS that allow us to interact with the ENS DApp. For this example, we will use the mycrypto.com interface, in conjunction with MetaMask as our wallet.
First, we need to make sure the name we want is available. While writing this book, we really wanted to register the name mastering.eth, but alas, Searching for ENS names on MyCrypto.com revealed it was already taken! Because ENS registrations only last one year, it might become possible to secure that name in the future. In the mean time, let’s search for ethereumbook.eth:
Great! The name is available. In order to register it, we need to move forward with Starting an auction for an ENS name. Let’s unlock MetaMask and start an auction for ethereumbook.eth:
Warning
|
As mentioned in Vickrey Auctions, you must reveal your bid within 48hrs after the auction is complete, or you lose the funds in your bid. Did we forget to do this and lose 0.1 ETH ourselves? You bet we did. Put a reminder on your calendar. |
Let’s make our bid. In order to do that we need to follow the steps in Placing a bid for an ENS name:
Take a screenshot, save your secret phrase (as a backup for your bid), and add a reminder in your calendar for the reveal date and time, so you don’t forget and lose your funds.
When you’re ready, select the big green submit button in the MetaMask transaction containing your bid:
If all goes well, return and reveal the bid in 48 hours and the name is now registered to your Ethereum address.
Now that we have registered an ENS name, we can manage it using another user-friendly interface, by visiting: https://manager.ens.domains/
Once there, enter the name you want to manage in the search box. You need to have your Ethereum wallet (e.g. MetaMask) unlocked, so that the ENS manager DApp can manage the name on your behalf using the The ENS Manager web interface:
From this interface, we can create subdomains, set a resolver contracts (more on that later) and connect each name to the appropriate resource, such as the Swarm address of a DApp front-end.
First, let’s create a subdomain for our example Auction DApp, (see Adding the subdomain auction.ethereumbook.eth). We will name the subdomain auction, so the fully qualified name will be auction.ethereumbook.eth:
Once you create the subdomain, you enter auction.ethereumbook.eth in the search box and now you can manage the subdomain, just as we managed the domain ethereumbook.eth previously.
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. By separating the functionality of resolvers from the naming system itself, we have a lot more flexibility. The owners of names can use custom resolvers to resolve any type or resource, extending the functionality of ENS. For example, if in the future you wanted to link a geolocation resource (longitude/lattitude) to an ENS name, you could create a new resolver that answers a geolocation query. Who knows what applications might be useful in the future? With custom resolvers, the only limitation is your imagination.
For convenience, there is a default public resolver that can resolve a variety of resources including:
-
Address (for wallets or contracts)
-
Content (a Swarm hash for DApps or contract source code)
Since we want to link our Auction DApp to a Swarm hash, we can use the public resolver, which supports content resolution, and don’t need to code or deploy a custom resolver. See Set the default public resolver for auction.ethereumbook.eth.
Let’s set our auction.ethereumbook.eth name to use the public resolver:
Once the resolver for auction.ethereumbook.eth is set to be the public resolver, we can set it to return the Swarm hash as the content of our name, see Set the 'content' return for auction.ethereumbook.eth:
Wait a short time for your transaction to be confirmed and you should be able to resolve the name correctly. Before setting a name, our Auction DApp could be found on a Swarm gateway by it’s hash:
or by searching in a DApp browser or Swarm gateway for the Swarm URL:
bzz://ab164cf37dc10647e43a233486cdeffa8334b026e32a480dd9cbd020c12d4581
Now that we have attached it to a name, it is much easier:
or, by search for "auction.ethereumbook.eth" in any ENS compatible wallet or DApp browser (e.g. Mist).
Over the past several sections, we have gradually built a Decentralized Application. We started with a pair of smart contracts to run an auction for ERC721 deeds. These contracts were designed to have no governing or privileged accounts, so that their operation is truly decentralized. We added a front-end, implemented in JavaScript, that offers a convenient and user friendly interface to our DApp. We added a decentralized storage system with Swarm, to store application resources such as images. We added a decentralized communication system using Whisper, to deliver a chat room function for each auction, without any central servers.
Next, we uploaded the entire front-end to Swarm, so that our DApp doesn’t rely on any web servers to serve the files. Finally, we use allocated a name for our DApp using ENS, connecting it to the Swarm hash of the front-end, so that users can access it with a simple and easy-to-remember human-readable name.
With each of these steps, we increased the decentralization of our application. The final result is a DApp the has no central point of authority, no central point of failure and expresses the "web3" vision.
Here’s the final Auction DApp architecture:
Decentralized applications are the culmination of the Ethereum vision, as expressed by the founders from the very earliest designs. While a lot of applications call themselves "DApps" today, most are not fully decentralized. However, it is already possible to construct applications that are almost completely decentralized. Over time, as the technology matures further, more and more of our applications can be decentralized, resulting in a more resilient, censorship resistant and free web.