Thank you for your interest in this post! We’re undergoing a rebranding process, so please excuse us if some names are out of date. Also have in mind that this post might not reference the latest version of our products. For up-to-date guides, please check our documentation site.
Updated Oct 6th 2017, for Truffle v3.4.11 and Solidity v0.4.15.
I’ve been working with smart contracts for 4 years, mainly in the Bitcoin blockchain. Some projects I participated in are Proof of Existence, bitcore, and Streamium. In the past months, I’ve been exploring and working with the Ethereum platform.
I’ve decided to compile a short guide to ease the way of future programmers learning Ethereum smart contract development. I’ve divided the guide in two sections: how to get started building smart contracts in Ethereum, and a quick note on smart contract security.
This guide assumes you have a basic level of technical understanding on how cryptocurrencies and blockchains work. If you don’t, I recommend skimming over Andreas Antonopoulos’ Mastering Bitcoin book, Consensys’ “Just Enough Bitcoin for Ethereum” guide, or at least watching this short video by Scott Driscoll. To continue ahead you should know what a public and private key are, why a blockchain needs miners, how decentralized consensus is reached, what a transaction is, and the concepts of transaction scripting and smart contracts.
Two other important and related concepts you’ll need to understand before working with Ethereum are the Ethereum Virtual Machine and gas.
Ethereum was designed as a smart contract platform. Its origin is actually linked to a critique made by Vitalik Buterin on bitcoin as a very limited smart contract platform. The Ethereum Virtual Machine (EVM) is where smart contracts run in Ethereum. It provides a more expressive and complete language than bitcoin for scripting. In fact, it is a Turing Complete programming language. A good metaphor is that the EVM is a distributed global computer where all smart contracts are executed.
Given that smart contracts run in the EVM, there must be a mechanism to limit the resources used by each contract. Every single operation that is executed inside the EVM is actually simultaneously executed by every node in the network. This is why gas exists. An Ethereum transaction contract code can trigger data reads and writes, do expensive computations like using cryptographic primitives, make calls (send messages) to other contracts, etc. Each of these operations have a cost measured in gas, and each gas unit consumed by a transaction must be paid for in Ether, based on a gas/Ether price which changes dynamically. This price is deducted from the Ethereum account sending the transaction. Transactions also have a gas limit parameter that is an upper bound on how much gas the transaction can consume, and is used as a safe-guard against programming errors that could deplete an account’s funds. You can read more about gas here.
So, you know the basics, let’s set everything up to code! To start developing Ethereum apps (or DApps, for decentralized applications, as many people like to call them), you’ll need a client to connect to the network. It will act as your window to the distributed network, and provide a view of the blockchain, where all the EVM state is represented.
There are various compatible clients for the protocol, the most popular being geth, a Go language implementation. However, it’s not the most developer-friendly. The best option I’ve found is the testrpc node (yes, the name sucks). Trust me, it will save you a lot of time. Install it and run it (you may need to prepend sudo depending on your setup):
You should run testrpc in a new terminal and leave it running while you develop. Each time you run testrpc, it will generate 10 new addresses with simulated test funds for you to use. This is not real money and you’re safe to try anything with no risk of losing funds.
The most popular language for writing smart contracts in Ethereum is Solidity, so we’ll be using that. We’re also using the Truffle development framework, which helps with smart contract creation, compiling, deployment and testing. Let’s start (again, you may need to prepend sudo depending on your setup):
Truffle will create all the files for an example project, including contracts for MetaCoin, a sample token contract.
You should be able to compile the example contracts by running truffle compile. Then, to deploy the contracts to the simulated network using the testrpc node we have running, you need to run truffle migrate:
Note to Mac OS X users: Truffle is sometimes confused by .DS_Store files. If you get an error mentioning one of those files, just delete it.
We just deployed the sample contracts to our testrpc node. Wohoo! That was easy, right? Time to create our own contract now!
In this guide we’ll be writing a simple Proof of Existence smart contract. The idea is to create a digital notary that stores hashes of documents as proofs of their existence. Use truffle create contract to get started:
$ truffle create contract ProofOfExistence1
Now open contracts/ProofOfExistence1.sol in your favorite text editor (I use vim with Solidity syntax highlighting), and paste this initial version of the code:
We’ll start with something simple but incorrect and move towards a better solution. This is a definition of a Solidity contract, which is like a class in other programming languages. Contracts have state and functions. It is important to distinguish two kinds of functions that can appear in a contract:
constant.
Transactional functions: functions that perform a state change in the contract or move funds. As these changes need to be reflected in the blockchain, transactional function execution requires sending a transaction to the network and spending gas.
Our contract above has one of each kind, marked in the documentation. We’ll see how the kind of function we’re using with modifies how we interact with the smart contract in the next section.
This simple version only stores one proof at a time, using the data type bytes32, or 32 bytes, which is the size of a sha256 hash. The transactional function notarize allows one to store the hash of a document in our smart contract’s state variable proof. Said variable is public, and is the only way a user of our contract has to verify if a document has been notarized. We’ll do that ourselves shortly, but first…
Let’s deploy ProofOfExistence1 to the network! This time, you’ll have to edit the migration file (migrations/2_deploy_contracts.js) to make Truffle deploy our new contract. Replace the contents with the following:
var ProofOfExistence1 = artifacts.require("./ProofOfExistence1.sol");
module.exports = function(deployer) {
deployer.deploy(ProofOfExistence1);
};
To run this migration again, you’ll need to use the reset flag, to make sure it runs again.
truffle migrate --reset
More on how Truffle migrations work can be found here.
Now that our contract is deployed, let’s play with it! We can send messages to it via function calls and read its public state. We’ll use the Truffle console for that:
First thing we do is obtain a representation of our deployed contract and store it in a variable called poe.
We then call the transactional function notarize, which involves a state change. When we call a transactional function, we get a Promise that resolves to a transaction object, not what the actual function returns. Remember that to change the EVM state we need to spend gas and send a transaction to the network. That’s why we get a transaction information object as the result of the Promise, referring to the transaction that did this state change. In this case, we are not interested in the transaction id, so we just discard the Promise. When writing a real app, we’ll want to save it to check the resulting transaction and catch errors.
Next, we call the read-only (constant) function proofFor. Remember to mark your read-only functions with the keyword constant, or else Truffle will try to craft a transaction to execute them. This is a way to tell Truffle that we’re not interacting with the blockchain but just reading from it. By using this read-only function, we obtain the sha256 hash of the ‘An amazing idea’ “document”.
We now need to contrast this with the state of our smart contract. To check if the state changed correctly, we need to read the proof public state variable. To get the value of a public state variable, we can call a function of the same name, which returns a Promise of its value. In our case, the output hash is the same, so everything worked as expected 🙂
For more info on how to interact with contracts, read this section of the Truffle documentation.
As you can see from the snippet above, our first version of the Proof of Existence smart contract seems to be working! Good work! It’s only good for registering one document at a time, though. Let’s create a better version.
Let’s change the contract to support multiple document proofs. Copy the original file with the name contracts/ProofOfExistence2.sol and apply these changes. The main changes are: we change the proof variable into a bytes32 array and call it proofs, we make it private, and we add a function to check if a document has already been notarized by iterating that array.
Let’s interact with the new functions: (remember to update migrations/2_deploy_contracts.js to include the new contract and run truffle migrate --reset)
This version is better than the first, but still has some problems. Note that every time we want to check if a document was notarized, we need to iterate through all existing proofs. This makes the contract spend more and more gas on each check as more documents are added. A better structure to store proofs is a map. Luckily, Solidity supports maps, and calls them mappings. Another thing we’ll improve in this version is removing all that extra comments marking read-only or transactional functions. I think you get it by now 🙂
Here’s the final version, which should be pretty easy to understand because you followed along previous versions:
That looks good enough. And it works exactly as the 2nd version. To try it out, remember to update the migration file and run truffle migrate --reset again. All the code in this tutorial can be found in this GitHub repo.
Once you’ve tested your contract extensively using testrpc in the simulated network, you’re ready to try it in the real network! To do that, we need a real testnet/livenet Ethereum client. Follow these instructions to install geth.
During development, you should run nodes in testnet mode, so that you can test everything out without risking real money. Testnet mode (also known as Morden in Ethereum) is basically identical to the real Ethereum, but the Ether token there has no monetary value. Don’t be lazy and remember to always develop in testnet mode, you will regret it if you lose real Ether due to a programming error (trust me on that, hehe).
Run geth in testnet mode, with RPC server enabled:
geth --testnet --rpc console 2>> geth.log
This will open a console where you can type in basic commands to control your node/client. Your node will begin downloading the testnet blockchain, and you can check progress by checking eth.blockNumber. While the blockchain is downloading, you can still run commands. For example, let’s create an account: (remember the password!)
> personal.newAccount() Passphrase: Repeat passphrase: "0xa88614166227d83c93f4c50be37150b9500d51fc"
Let’s send some coins there and check the balance. Just copy-paste the address you just generated and this faucet will send you some testnet Ether. To check you balance, run:
> eth.getBalance(eth.accounts[0]) 0
It will show no balance because your node hasn’t synced with the rest of the network yet. There, you can also see the current testnet highest block number (#1819865 at the time of writing), which you can use in combination with eth.blockNumber to know when your node has synced completely.
Once your node has synced, you’re ready to deploy the contracts to the testnet using Truffle. First, unlock your main geth account, so that Truffle can use it. And be sure that it holds some balance, or you won’t be able to push a new contract to the network. On geth run:
> personal.unlockAccount(eth.accounts[0], "mypassword", 24*3600) true > eth.getBalance(eth.accounts[0]) 1000000000000000000
Ready to go! If some of these two are not working for you, check the steps above and make sure you’ve completed them correctly. Now run:
$ truffle migrate --reset
Note that this time, it will take longer to complete, as we’re connecting to the actual network and not one simulated by testrpc. Once it completes, you can interact with the contract using the same approach as before.
The testnet-deployed version of ProofOfExistence3 can be found at the address 0xcaf216d1975f75ab3fed520e1e3325dac3e79e05. (Feel free to interact with it and send your proofs!)
I’ll leave the details on how to deploy to the live network to the reader. You should only do this once you’ve extensively tested your contracts in simulated and test networks. Remember any programming error can result in monetary loss in the livenet!
“Smart contracts are pretty difficult to get right.” Emin Gün Sirer.
Given that by nature, smart contracts are computer code that define how money moves, I couldn’t end this guide without even a small note on security. I’ll be talking about smart contract security in much more depth at future posts (edit: like this one), but here are some quick notes to get you started.
Some problems you should be aware of (and avoid):
These are provided just as examples of unexpected behaviors that can lead for theft or destruction of funds in your smart contract. The moral is: if you’re writing smart contracts, you’re writing code that handles real money. You should be very careful! Write tests, do code reviews, and audit your code.
The best way to avoid obvious security problems is to have a solid understanding of the language. I recommend you read the Solidity documentation if you have the time. We still need better tools for acceptable smart contract security. (Edit: close to the original publishing of this post, we launched the OpenZeppelin library, and we’ve recently announced zeppelinOS.
That’s it! I hope you enjoyed reading this guide and learning your first steps in programming smart contracts in Ethereum! This is still a very new industry and there’s lot of room for new apps and tools. Feel free to contact me with ideas or prototypes.