The Blockchain Explained to Web Developers, Part Two: In Practice
The marmelab blog
Is the blockchain a revolution? The technology that powers Bitcoin sure has the potential to disrupt the entire Internet, as we explained in a previous post. But how can you, a developer, use the blockchain to build applications? Are the implements effortless to use, despite the complexity of the underlying concepts? How good is the developer practice?
We dreamed to find out, and there is no better tutorial than developing an app from scrape. So we’ve made a elementary decentralized ad server called Zero Dollar Homepage, powered by blockchain. This is the story of our practice. Read on to learn how hard the blockchain is for developers today.
The blockchain shines when it substitutes intermediaries. We chose to concentrate on Ad Platforms, which are intermediaries inbetween announcers (who buy visibility) and content providers (who sell screen real estate). Our project was to build a decentralized ad platform running on the blockchain.
Since the famous Million Dollar Homepage experiment, innovating in the field of paid ads can’t make you rich anymore.
Instead, we chose to build a instrument that permits to display ads for free – a Zero Dollar Homepage. For free, but not for nothing: advertisers exchange ad visibility for open-source contributions. So we’ve built a decentralized app to manage how ads display on a particular page. Advertisers need to take up a coding challenge to be able to put their ads on this page.
In concrete terms, whenever we merge a Pull Request (PR) on one of marmelab’s open-source repositories, a GitHub bot comments on the PR, and invites the PR author to publish their ad on the ad platform admin panel.
Following the link contained in the comment, the PR author is asked to sign in with their GitHub credentials. Then, they can upload an ad – in fact, a elementary photo. This pic is added to the list of photos uploaded by other PR authors, in chronological order.
Each day at midnight, an automated script takes the next pic in line (using a FIFO ordering), and displays it on http://marmelab.com/ZeroDollarHomepage/ for the next twenty four hours.
Note: The entire process requires no intermediary, but in order to avoid the display of adult imagery on our site, we validate the uploaded photos through the Google Vision API before putting them online.
Here is how we separated responsibilities in each of the four use cases of an ad platform:
- Open-source contributor notification Whenever an open-source PR gets merged on one of our repositories, GitHub notifies the admin app with the PR details. The app publishes a comment on the PR to notify the contributor. The comment contains a link back to the admin app, with the PR details.
- Claim and photo upload Following the comment link, the contributor goes to the admin app. He must sign in with his GitHub credentials to be authenticated. The admin app then calls GitHub to grab the PR details, and to check that the contributor is actually the PR author. If it’s OK, the admin app displays an picture upload form. When the contributor uploads an photo, the admin app thrusts the PR id to the blockchain, and uploads the picture to a CDN (named after the PR id). The admin app displays the approximate date of publication of the picture based on the number of valid PRs with an picture still waiting in the blockchain.
- Ad placement Every twenty four hours, a cron asks the blockchain for the next PR not yet displayed. The blockchain marks this PR as displayed and sends the ID. The cron renames the photo named after the pr ID to “current image”.
- Ad display Each time a visitor wants to display the ad in ZeroDollarHomepage, it asks the CDN for the current pic. It happens to be the latest published ad from the blockchain, which remains displayed at least one day (and until another contributor claims a PR).
This might seem surprising, as the blockchain plays a very puny part in the process. But we quickly realized that the entire code of the ad platform couldn’t live in the blockchain. In fact, blockchains have very limited capabilities in terms of connectivity to the Internet, and processing power. So we delegated only the crucial ad placement tasks to the blockchain:
- Register a pull request by an authenticated contributor
- Get the last non displayed pull request, and mark it as displayed
Other tasks ended up in the admin app, outside of the blockchain, for various reasons:
- Register a pull request from a webhook Registering a pull request before it’s been claimed is futile, since the contributor may never claim it. Besides, storing data in the blockchain isn’t free, so we store only what we have to store. The downside is that any PR on our public repositories, including those created before this experiment, are eligible for the next step.
- Notify the user by posting a comment to GitHub A wise contract can’t call an outer API, so it’s just not possible. Instead, we delegated this task to the admin app.
- Verify a claimed PR’s author Again, a wise contract can’t call the GitHub APIs. Instead, we moved this logic to the admin app, and made it a prerequisite before calling the blockchain.
- Store the Picture In theory, you can store pretty much anything in the blockchain, including pictures. In practice, photos cost a lot to store, and we didn’t manage to store more than one “table” (array of data) in our clever contract.
- Update the displayed ad to the next in line A blockchain has no equivalent of the setTimeout function, or cron jobs. You might however execute some code every x blocks but it’s not related to time. Instead, we used a cron-like library on our API.
Research, documentation and very first attempts
As we explained in a previous post, they aren’t many good choices when choosing a blockchain network. So we chose Ethereum.
We quickly hit our very first wall. Until a few weeks ago, you couldn’t play with the Ethereum blockchain without buying Ether very first, even for elementary tests. Also, Ethereum didn’t truly permit private blockchains in its former version (named Frontier), which made development very complicated. Anyone accessing the Ethereum network might call your test contracts. More importantly, the documentation is a volunteer initiative, and was not in sync with the development state.
Note: Ethereum bumped their version since we developed the application, switching from Frontier to Homestead. The Ethereum community improved the documentation quality for Homestead.
Despite these shortcomings, we managed to register three knots on the Ethereum network across Nancy, Paris and Dijon, and to share a ping inbetween those knots.
In the course of our documentation search, we eventually found the Eris documentation. Eris did an excellent job at explaining Blockchains and contracts. Moreover, they especially built a layer on top of Ethereum, and open-sourced a bunch of instruments to ease the process of developing clever contracts.
eris is directive line device you can use to initialize any number of local blockchains you need.
Brainy Contract Implementation
A clever contract is very similar to an API. It has a few public functions which might be called by anyone registered on the blockchain network. Unlike an API, a brainy contract cannot call outer web APIs (a blockchain is a closed ecosystem). A brainy contract may however call other brainy contracts, provided it knows their address.
As with an API, the public functions are only the peak of the iceberg. A contract might be in fact composed of many private functions, variables, etc.
Brainy contracts are hosted in the blockchain in an Ethereum-specific binary format, executable by the Ethereum Virtual Machine. Several languages and compilers are available to write contracts:
The Zero Dollar Homepage Contract
The Zero Dollar Homepage contract stores the claimed pull-requests, and a queue of requests to display. The very first version of the Solidity contract looked like this:
For this very first attempt, we used the Eris JS libraries to communicate with our blockchain. Instanciating a contract from a Knot.js file turned up to be as ordinary as:
And calling it wasn’t difficult either:
For more information about the Eris JS cording libraries, please refer to Eris documentation.
Unit Testing Contracts
We love Test Driven Development, and one of the very first question we had was: how can we test a Solidity clever contract?
The Eris guys made a device for that, too: sol-unit. It runs a fresh local blockchain network for each test, in a docker container (which ensures each test run in a clean environment), and executes the test. Tests are written as a contract, too. Neat!
Well, not so rapid. sol-unit is an npm package, and to use the testing functions (assertions, etc.), we had to import the contract supplied by this package in our testing contracts. For that, there is a elementary Solidity syntax:
So far so good… or not. We hit a strange case when compiling our contracts. Evidently, you can’t import contracts with such a path. We ended up adding a guideline in our test makefile target to copy those sol-unit contracts in the same folder as ours. After that, running sol-unit was plain and we commenced coding.
Running a Test Blockchain
Running a blockchain and deploying our contract to it was as elementary as following the Eris documentation. We managed to resolve the few troubles we met using a bunch of guidelines that we integrated in our makefile. The entire process of running a fresh blockchain with our contract looks like this:
- Reset any running eris docker containers, and liquidate some improvised files
- Commence the eris key service
- Generate our account key, and store its address in a convenient file to be loaded later by the JS API
- Generate the genesis.json , which is the “block 0” of the blockchain
- Create and begin the fresh blockchain
- Upload the contract to the blockchain and save its address in order to call it when we need it
After a few days of work, we were able to run the contracts on a local Eris blockchain.
From Eris to Ethereum
At this point, we desired to attempt out our contracts on a local Ethereum blockchain.
To communicate with contracts inwards the Ethereum blockchain, we had to use the Web3 libraries. We learned a lot while attempting to use them. We realized that eris was hiding a lot of the underlying complexity.
Very first, our initial assumption that a contract is similar to an API was not correct. We had to distinguish functions that were only reading data from the blockchain, and functions that were writing data to it.
The very first kind (read-only functions) would comeback the resulting data asynchronously, just like an API would do. The 2nd kind (write functions) would only comeback a transaction hash. The expected side effects of a write function (switches inwards the blockchain) wouldn’t be effective until the corresponding blocks would be mined, which could take some time (from ten seconds to one minute in the worst case). Moreover, we haven’t been able to make those writing functions come back values, so we had to switch our solidity code to call a write function very first, then call a read function to get the results.
We also discovered events, which can be used to be notified when something happens in a wise contract. The brainy contract is responsible for triggering the events. They look like this with solidity:
And they can be triggered from any of the brainy contract functions, like this:
Those events are stored permanently in the blockchain. That means we could use the blockchain as an event store. It might be the easiest way to determine if a call to a function has been successfully executed: the wise contract may trigger an event at the end of its process with failure reasons, results of computation, etc… It’s worth noting that some integration packages for Meteor are already available.
Eventually, we refactored our clever contracts to be a lot simpler in order to get almost the same features. We had to get rid of the mappings (which we haven’t been able to use – our transactions weren’t mined by the Ethereum network for some reason).
The Ethereum implementation looks like this:
The process for claiming a pull request and returning the estimated display date evolved to become:
And with this code, our decentralized app worked in a local Ethereum network.
Deployment to Production
Running our application in a local environment was a challenge, but deploying it to production, in the real Ethereum network, was a battle.
There are a few gotchas to be aware of. The most significant one is that contracts are immutable in code. This means that:
- A contract that you deploy to the blockchain stays there forever. If you find a bug you your contract, you can’t fix it – you have to deploy a fresh contract.
- When you deploy a fresh version of an existing contract, and any data stored in the previous contract isn’t automatically transferred – unless you voluntarily initialize the fresh contract with the past data. In our case, fixing a bug in the contract actually wipes away recorded PRs (whether already advertised, or waiting for ad display).
- Every contract version has an id (for example, the current ZeroDollarHomepage contract is 0xd18e21bb13d154a16793c6f89186a034a8116b74). Since past versions may contain data, keep track of past contract ids if you don’t want to lose the data (this happened to us, too).
- As you can’t update a contract, you can’t rollback an update either. Make truly sure that your contract works before redeploying it.
- When you deploy a fresh version of an existing contract, the old (buggy) contract can still be called. Any system outside of the blockchain referencing the contract (like our Knot admin app in Zero Dollar Homepage) must be updated to point to the fresh contract. We left behind to do it a few times, and scraped our head despairingly to understand why our fresh code didn’t run.
- Contracts authors can kill their contract if they include a suicide call in the code. But all the existing transactions of the contract remain in the blockchain – forever. Also, make sure that the kill switch deals with the remaining ether in the contract if you don’t want it to vanish.
Another gotcha is that every contract deployment and write operation in the blockchain costs a variable amount of ether. We managed to get five ETH (more about getting ether below), but we had no idea how much we would need to deploy our contract, or calling a transaction. It’s stiffer to test when each failed test costs money.
For the Knot.js part, we determined to run it on an AWS EC2 example, like most of our projects. To do so, we had to:
- Run an Ethereum knot on the server
- Download the entire blockchain to this server
- Unlock an account with some Ether on the knot
- Deploy our application and link it to the knot
- Register our wise contract into the blockchain through the knot
Make sure your blockchain knot server has slew of storage. The current size of the blockchain is about 15GB. The default volume size on an EC2 example is 8GB (breathe). We had many troubles because we hadn’t downloaded the entire chain (but we didn’t realize it instantly). For example, we had an account with five ETH, but for a long time the system responded as if we hadn’t unlocked our account, or as if we had no ether. Downloading the rest of the chain immobilized this issue.
Likewise, unlocking our precious account containing five ETH was not an effortless task. We did not want to hardcode our passphrase in the application, and we desired to run the knot with supervisord to ease the deployment. We ultimately found a way that permitted us to switch the configuration without exposing our passphrase with the following supervisord configuration:
One final security note: The Remote Procedure Call (RPC) port of the blockchain is eight thousand five hundred forty five . Do not open this port on your EC2 example! Anyone knowing the example IP could control your Ethereum knot, and steal your ether.
Ether and Gas
As explained in our very first post on the blockchain, deploying and calling a contract in the Ethereum blockchain isn’t free. Since a blockchain is expensive to run, any write operation must be paid for. In Ethereum, the price of calling a write contract method depends on the complexity of the method. Ethereum comes with a list of Gas Fees, which tells you how much Ether you should add to a contract call to have it executed.
In practice, that’s a very low amount of Ether, a fraction of a single Ether. The Ethereum blockchain introduced another currency for running contracts: Gas.
1 Gas = 0.00001 ETH one ETH = 100,000 Gas
The Gas to Ether conversion rate will vary in the future according to the supply of computing power, and the computation request.
Charging a fee to process a transaction isn’t compulsory, but recommended. The Ethereum documentation says: “Miners are free to disregard transactions whose gas price is too low”. However, a mined block always give five ETH to the successful miner.
To call our own contracts, the Ethereum blockchain requires inbetween 0.00045 and 0.00098 Ether (the price depends on the gas price and the gas used by the transaction).
How do you get Ether and Gas? You can buy Ether (mostly by exchanging Bitcoins), or you can mine it. In France, where we live, buying Bitcoins or Ether requires almost the same procedure as opening a bank account. It’s slow (a few days), painful, and depends on exchange rates stationary by suggest and request.
So we determined to mine our Ether. That’s a good way to see if mining is profitable on Ethereum or not. We spawned a strenuous Amazon EC2 example, with strong GPU computing power (a g2.2xlarge example). The price of this example is 17$ per day. We installed Ethminer , and embarked our knot. We quickly had to beef up the example even more, because of high memory and storage requirements. The very first thing a knot does when it joins a blockchain is to download the entire history of past transactions. That takes a thick amount of storage: over 14GB for the blockchain’s history, and about 3GB for the Ethash Proof of Work.
Once our Ethereum knot embarked, we had to mine for three days to create a valid block:
As a reminder, the Ethereum blockchain mines one block every ten seconds. Mining a block brings up five Ether, which sell for harshly $55. The running cost for our muscled EC2 example for these three days was about $51. All in all, it was cheaper to mine Ether on AWS than to buy it. But we were very fortunate: since we mined our block, the network’s difficulty was multiplied by three.
How long can we run the ZeroDollarHomePage with five Ether? Let’s make the computation.
The Zero Dollar Homepage workflow implies one transaction per day, plus one transaction per claimed PR. Supposing contributors claim one PR per day, the yearly price in ether for running the platform would be at most three hundred sixty five * two * 0,00098 = 0.72 ETH. With five ETH, we should normally be able to run the platform for almost seven years.
As you see, running a contract in Ethereum isn’t free, but at the current price of Ether, it’s still cheap. However, the Ether value varies a excellent deal. Since mining Bitcoin is becoming less and less profitable, some large Bitcoin mining farms switch to Ethereum. This makes mining stiffer, and makes Ether more expensive every day.
Eventually, our brainy contract ended up working fine in our real world Ethereum knot hosted on EC2.
But by the time we got there, Ethereum released their Homestead version, which brought a lot of fresh things and broke our code entirely. It took us about a week to understand why, through trial and error, and fix the code that wasn’t compatible anymore for obscure reasons.
Peak: The Homestead release documents a hidden Ethereum feature, private networks, to ease development. The lack of private networks was one of our reasons to use Eris in the very first place.
The ZeroDollarHomePage platform is now up and running again. You can use it by opening a pull request on one of marmelab’s open-source repositories on GitHub, see the ads presently displayed on http://marmelab.com/ZeroDollarHomepage/, or browse the code of the application on marmelab/ZeroDollarHomePage. Yes, we’re open-sourcing the entire ad platform, so you can see in detail how it works, and reproduce it locally.
The Ethereum developer practice is very bad. Imagine that you have no logs and no debug implements. Imagine that the only way to detect why a program fails is to echo “I’m here” strings every line to locate the problem. Imagine that sometimes (e.g. in Solidity contracts), you can’t even do that. Imagine that a program that works flawlessly in the development environment (where you can add debug statements) fails silently in the production environment (where you can’t). That’s the developer practice in Ethereum.
If you store data in your clever contract, there is no built-in way to visualize the current state of this data after a transaction. That means you need to build your own visualisation implement to be able to troubleshoot errors.
The contraptions available to track Ethereum contracts and transactions are:
- etherscan.io: displays data about contracts, transactions, blocks
- etherchain.org: The blocks and ether information
- You can also get aggregated stats about the network and the knots
Each transaction (call to a contract method) is logged there, together with a trace of the contract execution… in machine language. Apart from making sure your call actually gets to the contract, you can’t use it for debugging.
Also, these devices can only monitor the public Ethereum network. Unluckily, you can’t use them to debug a local blockchain.
If you have ever seen Bitcoin transaction auditing sites, don’t expect the same level of sophistication for Ethereum. Besides, the bitcoin network only has one kind of transaction, so it’s lighter to monitor than a network designed to run brainy contracts.
And that’s not all: the Ethereum documentation is not in sync with the code (at least in the Frontier version), so most of the time we had to look at the libraries to attempt to understand how we’re expected to code. Since the libraries in question use a language that no one uses (Solidity), good luck figuring out how they work. Oh, and don’t expect help from Stack Overflow, either. There are too few people like us who dared to implement something serious to have a good community support.
Let’s be clear: we are not criticizing the Ethereum community for their lack of efforts. Once again, there is a tremendous momentum behind Ethereum, and things improve at a rapid rhythm. Kudos to all the documentation contributors for their work. But by the time we developed our application, the documentation state was clearly not good enough for a fresh Ethereum developer to begin a project.
You can find a few tutorials here and there, but most of the time, copy-pasted code from these tutorials simply doesn’t work.
Here are a few resources worth reading if you want to commence developing wise contracts yourself:
After Four weeks of work by two experienced developers, we managed to make our code work in the public Ethereum network with lots of effort. Regressions and compatibility violates in the Ethereum libraries inbetween Frontier and Homestead versions didn’t help. Check the project source code at marmelab/ZeroDollarHomePage for a detailed understanding of the internal workings. Please forgive the potential bugs in the code, or the inaccuracies in this post – we have a limited practice in the matter. Feel free to send us your corrections in GitHub, or in the comments.
We didn’t love the party. Finding our way across bad documentation and youthful libraries isn’t exactly our cup of tea. Fighting to implement ordinary features (like string manipulation) with a half-baked language isn’t joy either. Realizing that, despite years of programming practice in many scripting languages, we are not able to write a ordinary solidity contract is frustrating. Most importantly, the youth of the Ethereum ecosystem makes it fully unlikely to forecast the time to implement a elementary feature. Since time is money, it’s presently unlikely to determine how much it will cost to develop a Decentralized App.
In time and resources, ZeroDollarHomepage represents a development cost of more than €20,000 – even if it’s a very ordinary system. As compared to the implements we use in other projects (Knot.js, Koa, React.js, PostgreSQL, etc.), developing on the blockchain is very expensive. It’s a superb frustration for the dev team, and a strong signal that the ecosystem isn’t ready yet.
Is this bad practice sufficient to make up our mind about the blockchain? How come many startups showcase their blockchain services as successful innovations? What’s the real cost of building a DApp? Read the last post in this series to see what we truly think about the blockchain phenomenon.