Smart Contracts: The Working Parts
--
Tooling Up to write smart contracts — 2016 Edition
When you have completed this tutorial, you will have a *reliable* set of tools and techniques that you can use to:
- Develop ethereum smart contracts.
- Share them on the ethereum testnet.
This is not an article on the programming language solidity — those articles will come later in another series.
Table of Contents
- Status
- Audience
- Resources Required
- Step 1- Get the Ethereum CLI tools
- Step 2 — Launch a full Ethereum node
- Step 3 — Create and unlock an account
- Step 4 — Fully sync the testnet
- Step 5 — Mine some testnet ether
- Step 6 — Create the Hello World contract
- Step 7 — Create an interface to your smart contract
- Step 8 — Interact with your smart contract
- Step 9 — Get help
- Step 10- Contribute
Status
All steps in this tutorial tested working:
- 08/02/2016: Geth: 1.4.10-stable, Go: 1.6.2, OSX 10.11.5 by ZB — awarded one unicorn for bug reports
- 07/20/2016: Geth: 1.4.7-stable, Go: 1.6.2, OSX 11 by JD
- 07/19/2016: Geth: 1.3.6, Go: 1.6.1, OSX 10.9 by RSB
Audience:
Any developer with an interest in smart contracts who is comfortable with the command line and ethereum terminology.
This post is particularly recommended for developers who have given up in frustration with the state of ethereum development tools and documentation. We felt your pain. We tried every tool we could find. Finally, we settled on a reliable set. Currently, geth and the online solidity compiler are the most reliable options we have: they are free, work on any major platform, and come pre-built.
Once a year, we will update this series with a similar post.
Resources required:
You will need a modern computer with at least 20G free disk space and a broadband connection.
Total time spent on this tutorial should be less than one hour, with a break in the middle lasting a few hours to a day (depending on your bandwidth).
The break comes in during step four, which requires you to download the ethereum testnet blockchain. If you are serious about getting into this space, grabbing a copy of the testnet is well worth your time.
Step 1 — Get the ethereum CLI tools
The program “geth” implements a full ethereum node. We need one of those.
First, download and install geth from the Ethereum CLI tools download page here.
On the Mac, as of this writing, for those with brew already installed, all you need to do is:
brew tap ethereum/ethereum
brew install ethereum
Step 2 — Launch a full ethereum node
Launch geth as follows:
> geth --fast --cache=1024 --testnet --rpc --rpccorsdomain '*' console --testnet means "run on the testnet"
--rpc means "listen for rpc commands that come from outside"
--rpccorsdomain '*' "allow cross origin requests from anywhere (like the solidity compiler running on github.io)"
--fast "download big ass chunks of blockchain to go faster"
--cache=1024 "use tons of RAM to go faster" 'console' means launch an interactive geth console
Geth will produce copious logs on that terminal, making that a good place to examine logs, but a poor place to work on the console. Plan on leaving that terminal open, and running, for a long time.
In a totally new terminal, launch geth with the attach argument:
> geth attach
This will drop you into the geth console, where you will be doing most of your work (for this tutorial).
Tips:
*If “geth attach” fails with something like:
Fatal: Unable to attach to remote geth: dial unix /Users/you/Library/Ethereum/geth.ipc: connect: no such file or directory
Then geth probably didn’t properly create that pipe correctly (or your version might have put the pipe in a different place — seems to be platform/geth-version dependent). You have two options:
1. You can take the time to find the pipe that the first copy of geth created and specify that on the command line when you run “geth attach”, like so (hint: geth mentions the location of the pipe when you start it),:
geth attach ipc:/Users/you/Library/Ethereum/testnet/geth.ip
2. You can choose to punt, and just redirect the logs to a file for display in another terminal. This will make the geth console usable again, and you can run just the one copy:
geth --fast --cache=1024 --testnet --rpc --rpccorsdomain '*' console 2>> geth.log
*If you get stuck in geth, type: “exit” to get out.
*If you think you will do this more than once, make a little script to set it up:
bash-3.2$ echo "geth --testnet --rpc --rpccorsdomain '*' console" > ~/bin/rungeth_testnet.sh
bash-3.2$ chmod +x ~/bin/rungeth_testnet.sh
You don’t need the arguments for fast downloading once you have already donwloaded the blockchain once. During development, rather than conduct extensive testing on geth’s RPC connection, we have chosen to lock it down with a firewall when warranted.
Step 3 — Create and unlock an account
On the geth console, type:
personal.newAccount()
You will be asked for a passphrase. Provide one. Save it somewhere.
Passphrase:
"0x1aefd704facfda6069270ab69ef73e847aa897cc"
That long string is the address of your newly created account.
Now, lets unlock your account. In the geth console type:
eth.accounts
You will see the address of the account you just made. You must unlock that account to use it.
Using the address of the account you just made, and the password you used to create it, type:
personal.unlockAccount("0x9bcd2d4f8dddea8f4ff303fe2052fc134b655c8a", "your_password")
That should return “true”.
Now make an alias for your account so that you can reference it without having to paste the address all the time:
var me = eth.accounts[0];
undefined
me
"0x9bcd2d4f8dddea8f4ff303fe2052fc134b655c8a"
Tips:
*geth supports tab completion, so if you forget a command, type the part you know and hit tab and you’ll get a list:
> personal. <hit tab>
personal.listAccounts personal.newAccount personal.unlockAccount
*There is no account delete function in geth. Sometimes you need to clean house, though. To remove accounts, just encrypt the files and move them aside:
cd ~/Library/Ethereum/keystore/
ls -la
tar cvzf oldkeys.tgz *
openssl aes-256-cbc -a -salt -in oldkeys.tgz -out oldkeys.tgz.enc
ls
rm UTC*
rm oldkeys.tgz
mv oldkeys.tgz.enc ~/place_I_store_encrypted_stuff_that_gets_backed_up
*On a mac, geth will store your testnet account data in json files here:
~/Library/Ethereum/testnet/keystore/
*Click here to read the official Ethereum documentation on account management.
Step 4 — Fully sync the testnet
To continue, you are going to need to obtain the full testnet blockchain and some testnet ether. We’ll show you how to do that by syncing your blockchain and mining some ether.
Your copy of geth should already be downloading the full testnet from peers all over the world, because you launched it with the testnet flag. As mentioned earlier, syncing can take hours, and will require many gigabytes of data be downloaded. It only needs to be fully downloaded once.
The only workaround to syncing the blockchain is to find a friend you trust in physical proximity who can copy the testnet blockchain from their computer to your computer.
While your copy of geth downloads the blockchain, you will see blocks downloading in the geth console:
> I0802 22:01:40.381033 5591 downloader.go:288] Block synchronisation started
I0802 22:01:44.057910 5591 blockchain.go:889] imported 192 header(s) (0 ignored) in 1.148585631s. #192 [4be46f27… / e46144f5…]
And after a few hours (depending on bandwidth), you should see fewer blocks downloading:
I0718 18:57:19.859391 43671 blockchain.go:1251] imported 1 block(s) (0 queued 0 ignored) including 0 txs in 4.389402ms. #1333027 [2f0e3825 / 2f0e3825]
The message above imported only one block. That typically happens when you have a full blockchain downloaded — you are now downloading blocks as they are created in real time.
When your testnet blockchain is fully synced, you will see:
- Messages reporting that one block at a time is being imported.
- The block number in each message (looks like: #1333027) will match the highest block mined on ethernet block explorer sites (like: https://testnet.etherscan.io/)
Tips:
*If you don’t see blocks downloading, check to see if you have any peers with “net” commands on the console, like so:
net.listening
true
net.peerCount
10
*If you have zero peers, then you could have a firewall issue. You will need to be able to connect to port 30303 on other systems to discover peers. To get a little info from geth as to what ports it is using to communicate, type:
admin.nodeInfo
You will get a whole bunch of json info back, including a section telling you what port geth is using outbound to discover other systems:
ports: {
discovery: 30303,
listener: 30303
},
*Here is an alternate method of obtaining a copy of the testnet blockchain. It requires help from a friend.:
You can speed this up if you or someone you know has an up-to-date copy of the testnet blockchain, and a account with testnet ether they can share with you. That’s the ideal scenario, because you won’t have to wait for all this stuff.
To use this method, copy your friends /chaindata directory contents into your /chaindata directory.
Then, run geth as before, and have them send some ether to your account. A few blocks later on the testnet, your ether will appear.
*Testnet blockchain explorers:
Here are two good web services that will allow you to see your transactions on the testnet:
https://testnet.etherscan.io/
https://morden.ether.camp/
Step 5 — Mine some testnet ether
We need to mine testnet ether so that we can run transactions on the testnet
NOTE: You have to be fully synced before you can mine. Make sure you have the latest testnet block (Compare https://testnet.etherscan.io with your geth logs).
Mining on the testnet is another step that can take a while, although it will probably take an order of magnitude or two less time than syncing the blockchain. Mine one block, though, and you have enough testnet ether for thousands of experiments.
In the geth console, type:
miner.start()
Now check the geth log in the other terminal. After a few minutes, you should see some messages start to show up about “Generating DAG”. The “DAG” is a giant file that must be generated or obtained prior to mining. When you get close to having the full file generated, you will see the percentage approach 100 in the geth logs:
I0718 19:27:02.221854 45253 ethash.go:252] Generating DAG: 96%
I0718 19:27:08.514340 45253 ethash.go:252] Generating DAG: 97%
I0718 19:27:14.770954 45253 ethash.go:252] Generating DAG: 98%
And you will see work committed on new blocks by your CPU, like so:
I0718 19:38:53.697866 45253 worker.go:569] commit new work on block 1333201 with 1 txs & 0 uncles. Took 44.675404ms
On the testnet, it’s easy to mine. You should see a block get mined (giving you ether) in the logs within about 20 minutes (although that all depends on luck and CPU power). Here’s what it looks like when you successfully mine something:
I0718 19:46:48.537178 39568 worker.go:348] :hammer: Mined block (#1333236 / 065dff46). Wait 5 blocks for confirmation
Actually, it looks way cooler than that:
The ether will go into your primary account (after five blocks have confirmed it). Your primary account is your first account, unless you set another account as your primary account.
Unlock your account if it’s locked, and check your account balance via the geth console:
> eth.getBalance(me)
70757845080000109920
You’re rich!
Tips:
*What to do when geth stops working:
Check as often as practical to see if you are still downloading blocks. Your copy of geth will not always be able to find peers. If you see that it has stalled in downloading blocks for a long time, then exit geth and restart. You won’t lose any data — you will just need to give it a kickstart in finding new peers again. It will pick up where it left off. If you network connection is very bad, this will happen more often.
*Reminder as to how to unlock an account:
> personal.unlockAccount(me)
Unlock account 9bcd2d4f8dddea8f4ff303fe2052fc134b655c8a
Passphrase:
true
Step 6 — Create the Hello World contract
Open a web browser tab to the online solidity compiler: http://ethereum.github.io/browser-solidity/#version=soljson-latest.js
That compiler is almost an IDE. It has an editor and a bunch of other cool features — all of which work, in our experience.
First, paste this hello world contract into the online solidity compiler:
contract HelloWorld{
string public message;
function setMessage(string newMessage){
message = newMessage;
}
}
Next, on the right hand side of the online compiler, click on the Environment tab — that’s the tab that has a icon of a box on it, which appears between the paper airplane tab and the cloud upload tab.
Next, in the Environment tab, select the radio button for web3 provider, and make sure web3 provider endpoint is set to http://localhost:8545.
Finally, click the red “create” button.
You should see: “Waiting for transaction to be mined…” just below the red create button.
In the background, the online solidity compiler is communicating over port 8545 with geth, and using geth to put your contract on the testnet blockchain.
Once your transaction is mined, the online solidity compiler will show the gas cost to put this contract onto the blockchain.
Your contract is now deployed. You can interact with it through the web editor, or through the geth console. Let’s play with it on the geth console first.
Tips:
*If the solidity web compiler fails to connect to your local copy of geth, then you should check to make sure you can telnet to port 8545. Check your geth command line as well — do the options match the ones in step 2? It’s remotely possible that you have a firewall issue — but it’s probably some issue with geth. Sometimes geth just gets bogged down for a bit and after a minute it starts accepting connections again.
Step 7 — Create an interface to your smart contract
Back on the geth console, we’re going to put together a line of code that will require the contract address and interface. This will allow us to call the contracts functions. It’s a long string, so you may want to put it together in an editor. Of course, make sure your editor doesn’t mangle text — see tips at the end of this section.
The format of this line of code will be:
var helloWorld = eth.contract(YOUR INTERFACE STRING).at(YOUR CONTRACT ADDRESS)
Go ahead and copy that into your editor, and we’ll build the string as we go.
First, in the web solidity compiler, copy the text in the box “interface”.
The interface string will look something like this:
[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"setMessage","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"type":"function"}]
Back in your editor, replace YOUR INTERFACE STRING in the line of code with the interface string you copied from the editor.
Next, you need to grab the contract address that got created when you clicked the create button in the solidity web compiler.
You can grab that from the solidity web compiler in a line that starts with the name of the contract, and ends with the word blockchain. It looks like this:
Back in your editor, replace YOUR CONTRACT ADDRESS with the address string of your contract from the online solidity compiler. (Note that it should be in double quotes inside the .at() call)
Now you have built the line of code you need to create a contract object in geth.
Next, paste the line of code into geth. It will look a something like this:
var helloWorld = eth.contract([{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"setMessage","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"type":"function"}]
).at("0x9bcd2d4f8dddea8f4ff303fe2052fc134b655c8a")
You have created a handle to a contract object. That handle was defined by the contracts address, and it’s interface — exactly as you would expect.
Finally, in the console type:
> helloWorld
You should see something like this returned:
{
address: "0xafebc7b4b1d0d8bef87c20d9ed6b1739589a6a69",
allEvents: function(),
message: function(),
setMessage: function()
}
Tips:
*You see an error message like: (anonymous): Line 1:305 Unexpected token ILLEGAL
You have a typo, most likely.
The most likely reason is that you added or missed a quote or two.
The second most likely reason is that your editor mangles text. Perhaps your editor has “smart quotes” — quotes that look curvy and cute and in no way are interpreted as quotes by any program outside your editor. Also possible: your editor puts weird line breaks into text. This tutorial is for developers, so we know you can solve that. (And if we leave smart quotes in this blog post in any code section, or if we do anything else horrible like that, then please report the bug so we can send you your unicorn)
Step 8 — Interact with your smart contract
First, verify the variable “message” is currently unset, by calling:
helloWorld.message.call()
Next, set the variable “message” by running a transaction:
helloWorld.setMessage.sendTransaction("Greetings Blockchain", {from: eth.accounts[0]})
This will cost you a little bit of gas on the testnet, maybe 200,000. If we were on mainnet, that would be real money (a few US cents, perhaps).
You must wait for your transaction to be mined before this takes effect — thirty seconds should do it.
Once your transaction has mined, you can read the “message” variable again to see your message on the blockchain.
Next, type:
helloWorld.message.call()
You will see the message you set. Yay! You published working code and data to the ethereum blockchain
Next, lets read your data in the online solidity compiler, because less typing.
Go to the online solidity compiler, and click the blue “message” button. The compiler will use geth to retrieve your contract data from the blockchain. You will see it below the message button in the “Decoded:” field. Sweet!
Finally, you are probably wondering by now if you can share this with your friends, or if anyone can find this data on the ethereum testnet. Yup. It’s easy:
Open a tab to an ethereum testnet blockchain browser, like https://testnet.etherscan.io.
Copy your contract address from the online solidity compiler.
Paste it into the search box on etherscan.io.
There is the entire history of your contract, preserved for as long as the testnet is around.
Tips:
*Slow transactions:
I usually wait for at least two blocks to be mined before checking to see if my transaction has posted. Sometimes a transaction won’t get picked up in the very next block. Check your geth logs, and watch a couple transactions mine. It should take about 30 seconds for the network to mine two blocks. However, the mining power on testnet is extremely variable. If you are fully synced with the testnet, and your transaction is not mining (five minutes is too much), then consider mining yourself for a bit.
*Geth stopped running:
If you see something like:
> helloWorld.message.call()
dial unix /Users/rsb/Library/Ethereum/geth.ipc: connect: connection refused
at InvalidResponse (<anonymous>:-81662:-26)
at send (<anonymous>:-156322:-26)
at call (<anonymous>:-133322:-26)
at call (<anonymous>:-106881:-26)
at <anonymous>:1:1
Then the second copy of geth is running, but the first has stopped. Did you close that terminal? Did it crash? Fire up a new one and restart geth.
Step 9 — Get help
Here are a few places you can go to get help:
Mountain View Smart Contracts on Gitter — we check in when we can - stop by, have a cup of coffee with us, or leave a message.
Ethereum Gitter Channels — probably your best source for real-time help.
Other Ethereum Comms — IRC is active, as is Skype
Step 10- Contribute
If you find errors in this tutorial, please contribute bug reports on the MVSC github issue tracker. Unicorns are available for the most outstanding contributions. Thank you.
Get involved with the ethereum project on github.
Copyright 2016 Mountain View Smart Contracts