Backend software (application logic)
Frontend software
Data storage
Message communications
Name resolution
Because the business logic is controlled by a smart contract, a DApp backend will be fully distributed and managed on a blockchain platform. Unlike an application deployed on a centralized server, a DApp will have no downtime and will continue to be available as long as the platform is still operating.
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.
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.
A smart contract implementing ERC721 non-fungible “deed” tokens (DeedRepository)
A smart contract implementing an auction (AuctionRepository) to sell the deeds
A web frontend 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
pragma solidity^0.4.17;import"./ERC721/ERC721Token.sol";/*** @title Repository of ERC721 Deeds* This contract contains the list of deeds registered by users.* This is a demo to show how tokens (deeds) can be minted and added* to the repository.*/contractDeedRepositoryisERC721Token{/*** @dev Created a DeedRepository with a name and symbol* @param _name string represents the name of the repository* @param _symbol string represents the symbol of the repository*/functionDeedRepository(string_name,string_symbol)publicERC721Token(_name,_symbol){}/*** @dev Public function to register a new deed* @dev Call the ERC721Token minter* @param _tokenId uint256 represents a specific deed* @param _uri string containing metadata/uri*/functionregisterDeed(uint256_tokenId,string_uri)public{_mint(msg.sender,_tokenId);addDeedMetadata(_tokenId,_uri);emitDeedRegistered(msg.sender,_tokenId);}/*** @dev Public function to add metadata to a deed* @param _tokenId represents a specific deed* @param _uri text which describes the characteristics of a given deed* @return whether the deed metadata was added to the repository*/functionaddDeedMetadata(uint256_tokenId,string_uri)publicreturns(bool){_setTokenURI(_tokenId,_uri);returntrue;}/*** @dev Event is triggered if deed/token is registered* @param _by address of the registrar* @param _tokenId uint256 represents a specific deed*/eventDeedRegistered(address_by,uint256_tokenId);}
contractAuctionRepository{// Array with all auctionsAuction[]publicauctions;// Mapping from auction index to user bidsmapping(uint256=>Bid[])publicauctionBids;// Mapping from owner to a list of owned auctionsmapping(address=>uint[])publicauctionOwner;// Bid struct to hold bidder and amountstructBid{addressfrom;uint256amount;}// Auction struct which holds all the required infostructAuction{stringname;uint256blockDeadline;uint256startPrice;stringmetadata;uint256deedId;addressdeedRepositoryAddress;addressowner;boolactive;boolfinalized;}
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)
$ cd code/auction_dapp/backend $ truffle init $ truffle compile $ truffle migrate --network ropsten
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
$ npm install $ npm run dev
Store all the application code on Swarm or IPFS.
Access the DApp by reference to a name, using the Ethereum Name Service.
$ swarm version Version: 0.3 Git Commit: 37685930d953bcbe023f9bc65b135a8d8b8f1488 Go Version: go1.10.1 OS: linux
Maximum peer count ETH=25 LES=0 total=25 Starting peer-to-peer node instance=swarm/v0.3.1-225171a4/linux... 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=9c40be8b83e648d50f40ad3... 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...
$ swarm up code/auction_dapp/README.md ec13042c83ffc2fb5cb0aa8c53f770d36c9b3b35d0468a0c0a77c97016bb8d7c
$ cd code/auction_dapp/frontend $ npm run build > frontend@1.0.0 build /home/aantonop/Dev/ethereumbook/code/auction_dapp/frontend > node build/build.js Hash: 9ee134d8db3c44dd574d Version: webpack 3.10.0 Time: 25665ms Asset Size static/js/vendor.77913f316aaf102cec11.js 1.25 MB static/js/app.5396ead17892922422d4.js 502 kB static/js/manifest.87447dd4f5e60a5f9652.js 1.54 kB static/css/app.0e50d6a1d2b1ed4daa03d306ced779cc.css 1.13 kB static/css/app.0e50d6a1d2b1ed4daa03d306ced779cc.css.map 2.54 kB static/js/vendor.77913f316aaf102cec11.js.map 4.74 MB static/js/app.5396ead17892922422d4.js.map 893 kB static/js/manifest.87447dd4f5e60a5f9652.js.map 7.86 kB index.html 1.15 kB Build complete.
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
$ swarm --bzzapi http://localhost:8500 --recursive \ --defaultpath dist/index.html up dist/ ab164cf37dc10647e43a233486cdeffa8334b026e32a480dd9cbd020c12d4581
namehash([]) = 0x0000000000000000000000000000000000000000000000000000000000000000 namehash([label, ...]) = keccak256(namehash(...) + keccak256(label))
def namehash(name):
if name == '':
return '\0' * 32
else:
label, _, remainder = name.partition('.')
return sha3(namehash(remainder) + sha3(label))
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'))
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.
Migrate and upgrade the temporary ownership of the .eth TLD 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.
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.
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, which is 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 other things, 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 and the second-highest 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 bid. If you forgot to reveal you can make a late reveal and recover a little of your bid.
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.