Project Eth 1: Making an Ethereum Contract

Purpose

To learn how Ethereum works and set up your own local Etnereum testnet, followed by an Ethereum Smart Contract.

Ethereum Concepts (Simplified)

For much greater detail, see the Ethereum White Paper.

What You Need

An Ubuntu machine. I used an Ubuntu 14.04 virtual machine.

Exploring the Public Ethereum Blockchain

In a Web browser, go to

https://etherscan.io/

Live data about Ethereum appears, as shown below. When I did it (7-7-16), there were 81 million Ethers, worth $10 each, for a total market capitalization around $800 M. This makes Ethereum the second largest cryptocurrency--only Bitcoin is larger, with a market cap of about $12 B.

In the top center, hover the mouse over BLOCKCHAIN and click "View Pending Txns".

A short list of pending transactions appears, as shown below. Refresh your browser every few seconds to see how it works--transactions accumulate for about 30 seconds, then vanish, as the block is mined. The time between blocks varies quite a bit--I saw one block as long as 1 minute.

In the top section, hover the mouse over STATS and click "Block Count Chart".

A chart appears, as shown below. As you can see, Ethereum has approximately 6000 blocks per day -- approximately one every 30 seconds.

Installing geth

In a Terminal window, execute these commands to install the Ethereum software:
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install -y ethereum

Creating a Genesis Block

Execute these commands to make a genesis block:
cd
mkdir eth-test
cd eth-test
nano genesis.json
Paste in this code:
{
    "nonce": "0x0000000000000042",
    "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "difficulty": "0x4000",
    "alloc": {},
    "coinbase": "0x0000000000000000000000000000000000000000",
    "timestamp": "0x00",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "extraData": "Custem Ethereum Genesis Block",
    "gasLimit": "0xffffffff"
}

Save the file with Ctrl+X, Y, Enter.

Creating a Blockchain

Execute these commands to create the blockchain:
mkdir eth-data
geth --genesis genesis.json --datadir eth-data --networkid 123 --nodiscover --maxpeers 0 console
In the geth console, execute this command to make a new account:
personal.newAccount()
Enter a password twice to see your account address, as shown below. Make a note of this address.

Exit the geth console with:

exit

Allocating Ether to Your Account

Execute this command to edit the genesis block:
nano genesis.json
Edit the "alloc" secton to include your own address and a balance, as shown below.
{
    "nonce": "0x0000000000000042",
    "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "difficulty": "0x4000",
    "alloc": {
    "0x09c7b615a1c5b3016ff6b521723364aa9382ec6e": {
        "balance": "10000000000000000000"
   		}
   	},
    "coinbase": "0x0000000000000000000000000000000000000000",
    "timestamp": "0x00",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "extraData": "Custem Ethereum Genesis Block",
    "gasLimit": "0xffffffff"
}

Save the file with Ctrl+X, Y, Enter.

Deleting the Old Blockchain and Making a New One

Execute these commands to clear out the old blockchain data and restart geth:
cd eth-data
rm -rf chaindata dapp history nodekey
cd ..
geth --genesis genesis.json --datadir eth-data --networkid 123 --nodiscover --maxpeers 0 console
In the geth console, execute these commands.
primary = eth.accounts[0]
web3.fromWei(eth.getBalance(primary), "ether")
Your address appears, and your balance of 10 Ether, as shown below.

Starting Mining

Your blockchain isn't operating yet because it has no miners.

First execute this command to exit the geth console:

exit
To start mining, execute this command:
geth --mine --datadir eth-data --networkid 123 --nodiscover --maxpeers 0 console 2>>geth.log
Geth starts, as shown below.

Note: If you use Geth/v1.5.0-unstable, as shown in the image below, the project will fail. I modified the instructions so you should have a stable version 1.4.9 instead.

Execute this commands to see your balance:

primary = eth.accounts[0]
balance = web3.fromWei(eth.getBalance(primary), "ether")
You now have more than 10 Ether, as shown below.

Installing Solc

Our contract is written in the Solidity language. To use it, we need to install Solc, the Solidity compiler.

Open a new Terminal window, to see a Bash prompt ($). Execute this command:

sudo add-apt-repository ppa:ethereum/ethereum
A message appears saying "Press [ENTER} to continue. Press Enter.

Execute these commands:

sudo apt-get update
sudo apt-get install solc -y
which solc
You see a path to solc. Make a note of it. In the figure below, the path is

/usr/bin/solc

Return to the Terminal window showing the geth console. Execute these commands, adjusting the path in the first command to point to your solc location, if necessary.

admin.setSolc("/usr/bin/solc")
eth.getCompilers()
The response to the second command is ["Solidity"], as shown below, indicating that the compiler is ready to use.

Understanding the Greeter Contract

Here's the code for a very simple contract, with added comments. It contains two contracts:
contract mortal {
    /* Define variable owner of the type address*/
    address owner;

    /* this function is executed at initialization and sets the owner of the contract */
    function mortal() { owner = msg.sender; }

    /* Function to recover the funds on the contract */
    function kill() { if (msg.sender == owner) selfdestruct(owner); }
}

contract greeter is mortal {
    /* define variable greeting of the type string */
    string greeting;

    /* this runs when the contract is executed */
    function greeter(string _greeting) public {
        greeting = _greeting;
    }

    /* main function */
    function greet() constant returns (string) {
        return greeting;
    }
}

Unlocking your Account

To launch the contract, you need to spend some Ether, so you must unlock your account. In the geth console, execute these commands:
primary = eth.accounts[0]
personal.unlockAccount(primary)
Enter your password. After a pause of several seconds, the response is "true", as shown below.

Compiling your Contract

In the geth console, copy and paste in this command. It includes the whole source code of your contract, without comments, on one line.
var greeterSource = 'contract mortal { address owner; function mortal() { owner = msg.sender; } function kill() { if (msg.sender == owner) suicide(owner); } } contract greeter is mortal { string greeting; function greeter(string _greeting) public { greeting = _greeting; } function greet() constant returns (string) { return greeting; } }'
Only the end of the code is visible, and the reply is "undefined", as shown below.

In the geth console, execute this command.

var greeterCompiled = web3.eth.compile.solidity(greeterSource)
The reply is "undefined" again, as shown below.

Preparing your Contract for Deployment

You have defined two contract object types, mortal and greeter. Now you need to define the greeting message and launch a specific instance of the object. This will require you to spend some Ether. (We're using a test blockchain, so the Ether won't cost any real money.)

In the geth console, paste in these commands.

var _greeting = "Hello World!"
var greeterContract = web3.eth.contract(greeterCompiled.greeter.info.abiDefinition);

var greeter = greeterContract.new(_greeting, {from: eth.accounts[0], data: greeterCompiled.greeter.code, gas: 1000000}, function(e, contract){
  if(!e) {

    if(!contract.address) {
      console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");

    } else {
      console.log("Contract mined! Address: " + contract.address);
      console.log(contract);
    }

  }
})
A "Contract transaction send" message appears, and after a few seconds, you see "Contract mined!", as shown below.

Testing the Contract

In the geth console, execute these commands.
eth.getCode(greeter.address)
greeter.greet()
The first command returns a long address, and the second one returns "Hello World!", as shown below. The contract works!

Viewing the ABI

The Application Binary Interface is "a sort of user manual, describing the name of its functions and how to call them", according to the Contract Tutorial.

To see it, execute this command:

greeterCompiled.greeter.info.abiDefinition;
The object has two methods defined by its source code: "kill" and "greet", and a "constructor" (which is automatically called when an instance of the object is created), as shown below.

Making a Javascript Object

To make the greeter contract available to other machines on your Ethereum network, you need to create a Javascript object for them to call.

Execute this command, all on one line. Replace the address at the end with the your greeter's address, which you noted earlier.

var greeter2 = eth.contract([{constant:false,inputs:[],name:'kill',outputs:[],type:'function'},{constant:true,inputs:[],name:'greet',outputs:[{name:'',type:'string'}],type:'function'},{inputs:[{name:'_greeting',type:'string'}],type:'constructor'}]).at('0xcde7cfdf234dfa63ba4d7c273ac5cf67275bdf19');
Only a portion of that long command is visible, and the response is "undefined", as shown below.

To see the methods and attributes of the "greeter2" object, execute this command:

greeter2
It has the same two methods: "kill" and "greet", a constructor, and the address you specified, as shown below.

To run the greet method of the greeter2 object, execute this command:

greeter2.greet()
The reply is "Hello World!", as shown below.

Deleting the Contract

The Ethereum blockchain has abandoned contracts on it, which is wasteful. We're using a private testnet, but it's good policy to delete unused contracts anyway.

You can't do that with a simple call to a function like greet(), because adding or deleting something from the blockchain costs Ether. That requires a transaction, and you must specify which account will be used to pay the cost.

In the geth console, execute these commands, to delete the greeter contract and verify that it is gone.

greeter.kill.sendTransaction({from:eth.accounts[0]})
eth.getCode(greeter.contractAddress)
The first command returns a transaction ID, and the second one returns an "Invalid address" error, as shown below. The contract is gone.

Sources

How to setup a local test Ethereum Blockchain
ethereum/go-ethereum
A 101 Noob Intro to Programming Smart Contracts on Ethereum
Ethereum White Paper
Create a Hello World Contract in ethereum
Contract Tutorial
Okay, So I can run "geth --mine" and it appears that I am mining, how do I load up the wallet to check?
Why is my Greeter contract not mined on --testnet?
"Transaction w/ invalid nonce" error from eth_call RPC on develop branch - Issue #2771 - ethereum/go-ethereum - GitHub
Ethereum Frontier Guide


Posted 7-9-16 by Sam Bowne
Revised 7-12-16 12:26 PM